diff options
Diffstat (limited to 'vendor/gix-odb')
30 files changed, 7653 insertions, 0 deletions
diff --git a/vendor/gix-odb/.cargo-checksum.json b/vendor/gix-odb/.cargo-checksum.json new file mode 100644 index 000000000..cef2ac007 --- /dev/null +++ b/vendor/gix-odb/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CHANGELOG.md":"3f3de3d64895536d0c78ab2d34f16d39c368fa5d288307c0680f58b3c7764e71","Cargo.toml":"cb8e306c9a375ed4f2e266e98316742c028eff32ae00878e239a875e643172af","src/alternate/mod.rs":"02d29b6fbc6f4801a657882e19c01fa4aa7e7fd0f10dd25c8c040412468bb1de","src/alternate/parse.rs":"418f5c54d6293ceb886d44a3482dd3a2378d50031ff04186541c5e09be2eedcc","src/cache.rs":"b33c52a0ee7370ad9e81aed40cf74a9df8b60228edf0f2ea765112a7be130b9c","src/find.rs":"270a843d766f9f1641884c173d820c34ed8c59fa4dd6bce04f465b51888433d5","src/lib.rs":"2faebfc86388e0eeee119df148a616658d746e02d1cfe29c5417399d340ec3eb","src/sink.rs":"49b09d47c8194909781ce0a1d3ab55e5d411a8565ee105bc7d2e97b0531a5b12","src/store_impls/dynamic/access.rs":"0c1d4762953f76abc317e66261f3a5911c4bfcfe92ce75d22f69080a7e229501","src/store_impls/dynamic/find.rs":"63602bddfa71d3d5ee32cc258a36df8368621ed523a13479b69f28b1e04940aa","src/store_impls/dynamic/handle.rs":"fd203474b9097f19ab2ddabece78ed8abcb1a3e014475d814f8ff8da6ce42a07","src/store_impls/dynamic/header.rs":"9105fb3b11d73855b9bfc0309b2a0ba278bd3f86b93bedf01f6a2ee737565f00","src/store_impls/dynamic/init.rs":"4e701654bc265205dc9fcb9fa7eb848c99ce10d2c65e6254b7837a537102f1f3","src/store_impls/dynamic/iter.rs":"f4e52bb93946e5cd288097986cb50e7dc7cbdc8f6a0ac05099a90422682f508d","src/store_impls/dynamic/load_index.rs":"39155811df403cc3c51c7a953d94e3012098635932d78d9c985720d232127032","src/store_impls/dynamic/load_one.rs":"fa0393f14a653d9116c7b470219175cccffa8bb2e981afc89d62a48778a6d825","src/store_impls/dynamic/metrics.rs":"493f87f5bf72a23825c7c002f47edc286fff670ee3eb0ebdc60f52f131e674a4","src/store_impls/dynamic/mod.rs":"e70dc44156f5e9ef53fcb98892157f8a372d96a0946b70a736cf7f461386ed18","src/store_impls/dynamic/prefix.rs":"b1cd60744a3ae5d46c9f8388259504b8d106b3226283df7a77ba0ff42e43a0d4","src/store_impls/dynamic/types.rs":"c49e6ab0719a40bdee082da3d4169e5bf6be47f47cc695b4c343b87a4e21b1f6","src/store_impls/dynamic/verify.rs":"23938920dca4baa9bf604ead7756fec23b4afdc361fa6ad44f47cae72fa32d08","src/store_impls/dynamic/write.rs":"b281fbbc349377a7bd73b68370f7a508cddf35db4e1d4409fc0fff0ca8386d43","src/store_impls/loose/find.rs":"749273a7a3f82e889c7869ff6b1c6861d89c929ec4947ec5b358b002ff95b605","src/store_impls/loose/iter.rs":"52dd3df3148aa057efcbb2c86bb631834cbe9afdfb4a487fb6d1fe3e947b2c86","src/store_impls/loose/mod.rs":"9008a952ad41b7a9672028b6dd6d7fd613328694657f13e55ef078872ab8975e","src/store_impls/loose/verify.rs":"bbb840caca926d89d91c786ca232e3ae841eb16d44f7f5a9229bcd2198e2f7f8","src/store_impls/loose/write.rs":"7102a7248b169d9e783e3ba55c2f11a654abb0d51ce5f0308be3fc0eb3fa0f52","src/store_impls/mod.rs":"d5f123c2037dd5ee538721ad626aebf845d445d43438c3e10daac34d73fdc779","src/traits.rs":"9f77ef94dd7e021cceda22ba0929b6fa3a2e888acfd54f0dd4e8d6418dbda2b7"},"package":"e9a5f9e1afbd509761977a2ea02869cedaaba500b4e783deb2e4de5179a55a80"}
\ No newline at end of file diff --git a/vendor/gix-odb/CHANGELOG.md b/vendor/gix-odb/CHANGELOG.md new file mode 100644 index 000000000..a3e4bd266 --- /dev/null +++ b/vendor/gix-odb/CHANGELOG.md @@ -0,0 +1,2302 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.42.0 (2023-03-04) + +A maintenance release without user-facing changes. + +### Commit Statistics + +<csr-read-only-do-not-edit/> + + - 2 commits contributed to the release. + - 3 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + +<csr-read-only-do-not-edit/> + +<details><summary>view details</summary> + + * **Uncategorized** + - Prepare changelogs prior to release ([`895e482`](https://github.com/Byron/gitoxide/commit/895e482badf01e953bb9144001eebd5e1b1c4d84)) + - Release gix-features v0.28.0, gix-actor v0.19.0, gix-object v0.28.0, gix-diff v0.28.0, gix-traverse v0.24.0, gix-pack v0.32.0, safety bump 20 crates ([`0f411e9`](https://github.com/Byron/gitoxide/commit/0f411e93ec812592bb9d3a52b751399dd86f76f7)) +</details> + +## 0.41.0 (2023-03-01) + +### Bug Fixes + + - <csr-id-e14dc7d475373d2c266e84ff8f1826c68a34ab92/> note that crates have been renamed from `git-*` to `gix-*`. + This also means that the `git-*` prefixed crates of the `gitoxide` project + are effectively unmaintained. + Use the crates with the `gix-*` prefix instead. + + If you were using `git-repository`, then `gix` is its substitute. + +### Commit Statistics + +<csr-read-only-do-not-edit/> + + - 7 commits contributed to the release over the course of 2 calendar days. + - 11 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + +<csr-read-only-do-not-edit/> + +<details><summary>view details</summary> + + * **Uncategorized** + - Release gix-tempfile v4.1.0, gix-lock v4.0.0, gix-ref v0.25.0, gix-config v0.17.0, gix-url v0.14.0, gix-credentials v0.10.0, gix-diff v0.27.0, gix-discover v0.14.0, gix-hashtable v0.1.2, gix-bitmap v0.2.2, gix-traverse v0.23.0, gix-index v0.13.0, gix-mailmap v0.10.0, gix-pack v0.31.0, gix-odb v0.41.0, gix-transport v0.26.0, gix-protocol v0.27.0, gix-revision v0.11.0, gix-refspec v0.8.0, gix-worktree v0.13.0, gix v0.38.0, safety bump 6 crates ([`ea9fd1d`](https://github.com/Byron/gitoxide/commit/ea9fd1d9b60e1e9e17042e9e37c06525823c40a5)) + - Release gix-features v0.27.0, gix-actor v0.18.0, gix-quote v0.4.3, gix-attributes v0.9.0, gix-object v0.27.0, gix-ref v0.25.0, gix-config v0.17.0, gix-url v0.14.0, gix-credentials v0.10.0, gix-diff v0.27.0, gix-discover v0.14.0, gix-hashtable v0.1.2, gix-bitmap v0.2.2, gix-traverse v0.23.0, gix-index v0.13.0, gix-mailmap v0.10.0, gix-pack v0.31.0, gix-odb v0.41.0, gix-transport v0.26.0, gix-protocol v0.27.0, gix-revision v0.11.0, gix-refspec v0.8.0, gix-worktree v0.13.0, gix v0.38.0 ([`e6cc618`](https://github.com/Byron/gitoxide/commit/e6cc6184a7a49dbc2503c1c1bdd3688ca5cec5fe)) + - Adjust manifests prior to release ([`addd789`](https://github.com/Byron/gitoxide/commit/addd78958fdd1e54eb702854e96079539d01965a)) + - Prepare changelogs prior to release ([`94c99c7`](https://github.com/Byron/gitoxide/commit/94c99c71520f33269cc8dbc26f82a74747cc7e16)) + - Merge branch 'adjustments-for-cargo' ([`d686d94`](https://github.com/Byron/gitoxide/commit/d686d94e1030a8591ba074757d56927a346c8351)) + - Remove `num_cpus` dependency in favor of `std::thread::available_parallelism()` ([`9567102`](https://github.com/Byron/gitoxide/commit/9567102d4e1a729f7f9882f688365784b9813ac6)) + - Prepare for git-tempfile release ([`56c005b`](https://github.com/Byron/gitoxide/commit/56c005b13c44376f71e61781e73c0bf93416d0e4)) +</details> + +## 0.40.2 (2023-02-17) + +<csr-id-ebc7f47708a63c3df4415ba0e702660d976dfb3e/> +<csr-id-2290d006705ff47ad780b009fe58ee422b3285af/> +<csr-id-598698b88c194bc0e6ef69539f9fa7246ebfab70/> +<csr-id-46636e64c9a48ec0e85e014ac0cc8b48846d8462/> +<csr-id-e0b8636f96e4bfe1bc72b5aa6ad4c4c8538ff92c/> +<csr-id-5d57c1f7e3b9a84f7b46a4378015572155f3104b/> +<csr-id-47ca6ab2ff0cbf8801d0a82cebbbeb8c4f62cdae/> +<csr-id-2d6960f886c1165f0bdb6f2d653388e1e0b57a2d/> +<csr-id-424c9b3a2b467f5a1e339700257cd4ab72e2e692/> +<csr-id-b1c82a7959fba1541642fc8dfae46b27848f2ba3/> +<csr-id-9235106986e14551a28693bfe4ea92f046c65406/> +<csr-id-747a13e9a1fe5200c53055dd961507c9fef667e1/> +<csr-id-4c77e4c97641ab3b02b56aaa702a7d2ca5bced7c/> +<csr-id-d53c4b0f91f1b29769c9430f2d1c0bcab1170c75/> +<csr-id-b317200b72096573d511d229c6e61e74e7ba14db/> +<csr-id-eaae9c1bc723209d793eb93f5587fa2604d5cd92/> +<csr-id-13159eb972ed78ce4ebee2313b288023cec91c47/> +<csr-id-0092c256b3bfaf2818566540e660cdefcf68d246/> +<csr-id-9945eba749afb020e0deaaa5bb01fda6ff9ccd84/> +<csr-id-cfd8a25f9125c48afe4b66eab6b6ecf71097c486/> +<csr-id-1525f36d29574699d2fcb16b70678121030fd109/> +<csr-id-4ff21686c32a6edc84ea041c3040f33ae24f9519/> +<csr-id-91c8fc1f0c50af55d7cb233bbe813c6d12fe11bc/> +<csr-id-f7f136dbe4f86e7dee1d54835c420ec07c96cd78/> +<csr-id-c800fdd331e6d7a0b8d756ba822915259f26e9e8/> + +### Refactor (BREAKING) + + - <csr-id-ebc7f47708a63c3df4415ba0e702660d976dfb3e/> remove pack-cache from `Find::try_find(…)` + With the new architecture this can be an implementation detail without + forcing it to be Sync. + - <csr-id-2290d006705ff47ad780b009fe58ee422b3285af/> move git_pack::data::Object to git_object::Data, massively alter git_odb::Find trait + This will break a lot, but has to happen to prepare these traits for the + next generation of object databases. + - <csr-id-598698b88c194bc0e6ef69539f9fa7246ebfab70/> move loose header manipulation from git-pack to git-object + +### Bug Fixes (BREAKING) + + - <csr-id-1fabdc51b9468ba2c6b8cf74509ad5aa2a0b86f4/> `alternate::resolve(…)` now takes the current_dir as argument. + That way it's more consistent with similar low-level functions and it's + possible to avoid multiple calls to `std::env::current_dir()`. + + Furthermore, the usage of `current_dir()` is made explicit when + instantiating a store to allow it to be resued. + +### New Features (BREAKING) + + - <csr-id-d9d05b0db6b4453e7385117d466bf7c2e8de81fa/> add `Store::try_header()` for obtaining object information quickly. + Note that this feature also comes with various refactorings related to the error + type used by various methods in order to get away from a 'one error fits all' kind + of situation. + - <csr-id-3d8fa8fef9800b1576beab8a5bc39b821157a5ed/> upgrade edition to 2021 in most crates. + MSRV for this is 1.56, and we are now at 1.60 so should be compatible. + This isn't more than a patch release as it should break nobody + who is adhering to the MSRV, but let's be careful and mark it + breaking. + + Note that `git-features` and `git-pack` are still on edition 2018 + as they make use of a workaround to support (safe) mutable access + to non-overlapping entries in a slice which doesn't work anymore + in edition 2021. + - <csr-id-95210cb2ba85f75148b4ef48ccea9d9f8a0a0114/> Provide optional `candidates` for ambigious entries during `lookup_prefix()` + The candidate entries are all entries matching a given prefix. + - <csr-id-92d8be1101a7e76e70cd90db6a943b9e31e20802/> `loose::Db` and `Store` can return all candidate objects for a single prefix + This is the first step towards auto-disambiguating objects in rev-parse. + - <csr-id-bf73a94b43288b6634dbb33f2433656987a73baf/> `Cache::inner` removed in favor of `Deref/Mut` and `into_inner()` + Making the `inner` field available allows changing it, which would make + it potentially incompatible with existing caches. The new + implementation makes it essentially read-only while allowing more + convenient access to methods on `inner`. + +### Changed (BREAKING) + +<csr-id-ab4e726fcec65871a81056a9c69af8ea3f56b2a3/> +<csr-id-8bb5c9a75cd91ae0d888bc8e93707cfc9cc08090/> +<csr-id-580e96c1b2d9782a2e8cf9d1123f6d53a5376a3d/> +<csr-id-3f05fea55dc8acce1ed62ecbe4e0a1394f2720b7/> + + - <csr-id-8c5ae77f06a64c57df9a9ad1190266896a223dbe/> Remove deprecated compound and linked object databases + The dynamic/general store is the only maintained can-do-it-all + DB now. + - <csr-id-91d047658b114f372735116c9d8e6962a3873137/> cleanup and unify `verify_integrity()` method signature + Previously they used many different ways of handling their parameters + despite all boiling down to calling the same 'index::File::traverse()` + method. + + This allows for more reuse of `Options` structs and generally makes + clearer how these optinos are used. + - <csr-id-2ef9a8424af51310db8c1e6df31dde9953ed3d21/> Change accessors named `hash_kind()` to `object_hash()` for consistency + - <csr-id-49998cce419a27f3928ec4ac39da5e3b500e5cb2/> consistently use `object_hash` instead of `hash_kind` + - <csr-id-67c42fbf5f88f8dc42a9ebd7c6276d57ba1d4624/> remove `Write::*(…, hash_kind)` + The `hash_kind` is now intrinsic to the implementation of the write + trait and thus isn't passed along anymore in parameters. + + The `sink()` function now takes the kind of hash as parameter. + - <csr-id-ad1b9ea17eb4b98ebd2fddebe82a8fee1d63e9dd/> various changes to the `loose::Store` + - Change `path` field to read-only `path()` method + +### Refactor + + - <csr-id-46636e64c9a48ec0e85e014ac0cc8b48846d8462/> flatten errors into one + By adding one variant, one can remove the previous 'sub-error', for which + there is no precedent in the codebase yet. + - <csr-id-e0b8636f96e4bfe1bc72b5aa6ad4c4c8538ff92c/> replace bare u32 `data::Id` typedef + - <csr-id-5d57c1f7e3b9a84f7b46a4378015572155f3104b/> Use borrowed::Id in trees for full type safety + - <csr-id-47ca6ab2ff0cbf8801d0a82cebbbeb8c4f62cdae/> a simpler implementation to skip the header + +### Other + + - <csr-id-2d6960f886c1165f0bdb6f2d653388e1e0b57a2d/> try LRU-like contains implementation + Which unfortunately isn't really faster at all even though it totally + should be. + - <csr-id-424c9b3a2b467f5a1e339700257cd4ab72e2e692/> Try to make Handle usable for pack creation + It's nearly there, but for some reason the boxed dyn traits don't get to + be Send even though it's specified. + - <csr-id-b1c82a7959fba1541642fc8dfae46b27848f2ba3/> :Find for Arc and Rc + - <csr-id-9235106986e14551a28693bfe4ea92f046c65406/> :Find implementation for linked::Store + - <csr-id-747a13e9a1fe5200c53055dd961507c9fef667e1/> :borrowed::Object => git-odb::data::Object + - <csr-id-4c77e4c97641ab3b02b56aaa702a7d2ca5bced7c/> :Db::init() with a few tests + - <csr-id-d53c4b0f91f1b29769c9430f2d1c0bcab1170c75/> add link to simplified/polonius version in the docs + - <csr-id-b317200b72096573d511d229c6e61e74e7ba14db/> Only check alternates for objects not found in packs or loose + This matches the behavior of git. + - <csr-id-eaae9c1bc723209d793eb93f5587fa2604d5cd92/> Avoid double-lookup in packs without polonius + Split object lookup into two steps: looking up the object index, and + looking up the object itself given the index. This avoids passing in the + buffer (and thus looking like an unconditional borrow to non-polonius) + until we're committed to returning from the loop. + - <csr-id-13159eb972ed78ce4ebee2313b288023cec91c47/> try to get rid of tree-traversal Boxed error… + …which really complicates things downstream as these now have to deal + with another type argument, or of to try to turn it into a Box anyway. + + The latter seems to be…troubling so I can't make it compile. + - <csr-id-0092c256b3bfaf2818566540e660cdefcf68d246/> See if tree compaction saves considerable amounts of memory + No, it's not worth it. + - <csr-id-9945eba749afb020e0deaaa5bb01fda6ff9ccd84/> try to use a customized version of just pieces of Miniz-oxide + - <csr-id-cfd8a25f9125c48afe4b66eab6b6ecf71097c486/> fanout table, but slowly I get it :D + - <csr-id-1525f36d29574699d2fcb16b70678121030fd109/> discard idea of making traversal even more generic + - <csr-id-4ff21686c32a6edc84ea041c3040f33ae24f9519/> first silly attempt to randomly remove an allocation + - <csr-id-91c8fc1f0c50af55d7cb233bbe813c6d12fe11bc/> get rid of failure crate in favor of quick-error + +### Bug Fixes + + - <csr-id-4fffa9a9198cf3012fa8215796aab3d456519ff3/> remove panic-assertions in `loose` `lookup_prefix` + - <csr-id-1ce3190000f6211ce31468c7603d491bb5b90293/> Disable tag.gpgSign in test scripts + This is done for the same reason that commit.gpgsign is disabled for test + scripts. It prevents test failures if the user has tag.gpgsign enabled in + their global git config when invoking tests. + - <csr-id-41d494365d281056c5e9466860db808bd85143e9/> improve error messages when objects aren't found + - <csr-id-9c14de391a1a9f1055922164d1757c9aa9720807/> support Rust 1.52 + - <csr-id-b605c1fa0494b10872d3c2e6ecce0e39f1a90a9e/> linked::Store now assures unique IDs across compound stores + +### Chore + + - <csr-id-f7f136dbe4f86e7dee1d54835c420ec07c96cd78/> uniformize deny attributes + - <csr-id-c800fdd331e6d7a0b8d756ba822915259f26e9e8/> remove unused dependencies + +### Documentation + + - <csr-id-39ed9eda62b7718d5109135e5ad406fb1fe2978c/> fix typos + +### New Features + + - <csr-id-d792ea543246632bf1ca8d0e1d239bbe7f07e219/> use enumerations to advertise progress ids publicly. + Previously these were an implementation detail which also means they + couldn't be relied upon. + + Thanks to an intermediate enumeration, they become part of the public API + and their actual value is not exposed. + - <csr-id-e9d1f45e944e91bb9715a3ee89a4f28b09250411/> support for pack-order when iterating objects. + - <csr-id-7f19bd7e63d78e3151e43d5094ae9d35cbe34f46/> add `loose::Store::try_header()` to obtain loose object information without content. + - <csr-id-c8835c6edae784c9ffcb69a674c0a6545dbb2af3/> upgrade to `prodash 21.1` and add `Ids` to all progress instances. + That way callers can identify progress they are interested in, say, for + selective visualizations. + - <csr-id-b1c40b0364ef092cd52d03b34f491b254816b18d/> use docsrs feature in code to show what is feature-gated automatically on docs.rs + - <csr-id-517677147f1c17304c62cf97a1dd09f232ebf5db/> pass --cfg docsrs when compiling for https://docs.rs + - <csr-id-81e1a9d38aac9e6dd0618266ff826593e038cce8/> Add `Cache::has_object_cache()` and `Cache::has_pack_cache()` methods. + That way it's possible to conditionally set or change the cache size. + - <csr-id-84ec54e904378c5b3d7da9efff66b02e88b16916/> Handle::packed_object_count() + Provide packed objects numbers and cache the value + for fast access later on. + - <csr-id-996bfb3061fd9ee2cf38c93f39e0d4c7c6163386/> loose::Store::lookup_prefix(…) + - <csr-id-58c2edb76755ab71e10eef4cd9a51533825c291f/> git_pack::Find::try_find_cached(…, pack_cache) + With this method it's easier to bypass local caches and control + the cache oneself entirely. + - <csr-id-36fde720c34e02429a810ddd43b894a37516f51a/> add linked::Store::rc_iter() + For completeness in case of single-threaded operations + - <csr-id-a81b33359a4394a66f854195445f8f9aa0a46179/> linked::Store sorts bundles by modification date, newest first + - <csr-id-e25f4eadec679406aad6df10026e27e4832c2482/> A simplified version of the `Find` trait + It's meant for the next generation of object db handles which keep a + local cache of all the details of the actual object database. + +### Commit Statistics + +<csr-read-only-do-not-edit/> + + - 1569 commits contributed to the release over the course of 1041 calendar days. + - 60 commits were understood as [conventional](https://www.conventionalcommits.org). + - 26 unique issues were worked on: [#198](https://github.com/Byron/gitoxide/issues/198), [#222](https://github.com/Byron/gitoxide/issues/222), [#250](https://github.com/Byron/gitoxide/issues/250), [#254](https://github.com/Byron/gitoxide/issues/254), [#259](https://github.com/Byron/gitoxide/issues/259), [#260](https://github.com/Byron/gitoxide/issues/260), [#266](https://github.com/Byron/gitoxide/issues/266), [#279](https://github.com/Byron/gitoxide/issues/279), [#287](https://github.com/Byron/gitoxide/issues/287), [#293](https://github.com/Byron/gitoxide/issues/293), [#298](https://github.com/Byron/gitoxide/issues/298), [#301](https://github.com/Byron/gitoxide/issues/301), [#329](https://github.com/Byron/gitoxide/issues/329), [#331](https://github.com/Byron/gitoxide/issues/331), [#333](https://github.com/Byron/gitoxide/issues/333), [#364](https://github.com/Byron/gitoxide/issues/364), [#384](https://github.com/Byron/gitoxide/issues/384), [#427](https://github.com/Byron/gitoxide/issues/427), [#470](https://github.com/Byron/gitoxide/issues/470), [#536](https://github.com/Byron/gitoxide/issues/536), [#59](https://github.com/Byron/gitoxide/issues/59), [#63](https://github.com/Byron/gitoxide/issues/63), [#67](https://github.com/Byron/gitoxide/issues/67), [#691](https://github.com/Byron/gitoxide/issues/691), [#724](https://github.com/Byron/gitoxide/issues/724), [#XXX](https://github.com/Byron/gitoxide/issues/XXX) + +### Thanks Clippy + +<csr-read-only-do-not-edit/> + +[Clippy](https://github.com/rust-lang/rust-clippy) helped 38 times to make code idiomatic. + +### Commit Details + +<csr-read-only-do-not-edit/> + +<details><summary>view details</summary> + + * **[#198](https://github.com/Byron/gitoxide/issues/198)** + - Adjust all changelogs to fulfil requirements for publishing ([`04b9ca0`](https://github.com/Byron/gitoxide/commit/04b9ca025a1667529b2221ab4280bd3c8dae01cf)) + - Deduplicate conventional message ids ([`e695eda`](https://github.com/Byron/gitoxide/commit/e695eda8cd183f703d9a3e59b7c3c7fa496ea1d2)) + - Regenerate all changelogs to get links ([`0c81769`](https://github.com/Byron/gitoxide/commit/0c817690bd444f52bed2936b2b451cafd87dde92)) + - Mention actual issues that where worked on ([`a517e39`](https://github.com/Byron/gitoxide/commit/a517e39a81145b331f6c7a6cc2fc22e25daf42e2)) + - Allow 'refactor' and 'other' in conventional messages if they have breaking changes ([`4eebaac`](https://github.com/Byron/gitoxide/commit/4eebaac669e590beed112b622752997c64772ef1)) + - Rebuild all changelogs to assure properly ordered headlines ([`4a9a05f`](https://github.com/Byron/gitoxide/commit/4a9a05f95930bad5938d4ce9c517ebf0e0b990f1)) + - Sort all commits by time, descending… ([`f536bad`](https://github.com/Byron/gitoxide/commit/f536bad20ffbac4dc353dfeb1a917bb88becbb78)) + - Greatly reduce changelog size now that the traversal fix is applied ([`a0bc98c`](https://github.com/Byron/gitoxide/commit/a0bc98c06c349de2fd6e0d4593606e68b98def72)) + - Fixup remaining changelogs… ([`2f75db2`](https://github.com/Byron/gitoxide/commit/2f75db294fcf20c325555822f65629611be52971)) + - Generate changelogs with details ([`e1861ca`](https://github.com/Byron/gitoxide/commit/e1861caa435d312953a9fea7ceff6d2e07b03443)) + - Update all changelogs with details ([`58ab2ae`](https://github.com/Byron/gitoxide/commit/58ab2aee23ba70a536e9487b44fb04c610374d1a)) + - Update changelogs ([`c857d61`](https://github.com/Byron/gitoxide/commit/c857d61ce3ce342012a2c4ba10a8327822aa530e)) + - Avoid adding newlines which make writing unstable ([`6b5c394`](https://github.com/Byron/gitoxide/commit/6b5c394f49282a8d09c2a9ffece840e4683572db)) + - Fix section headline level ([`9d6f263`](https://github.com/Byron/gitoxide/commit/9d6f263beef289d227dec1acc2d4240087cb9be6)) + - Write first version of changlogs thus far… ([`719b6bd`](https://github.com/Byron/gitoxide/commit/719b6bdf543b8269ccafad9ad6b46e0c55efaa38)) + - Parse more user generated section content, adapt existing changelogs to work correctly ([`2f43a54`](https://github.com/Byron/gitoxide/commit/2f43a54298e7ecfff2334627df149fe0882b5d1d)) + * **[#222](https://github.com/Byron/gitoxide/issues/222)** + - Update changelogs prior to release ([`9a493d0`](https://github.com/Byron/gitoxide/commit/9a493d0651b0b6d71cf230dc510a658be7f8cb19)) + - Stabilize changelogs ([`920e832`](https://github.com/Byron/gitoxide/commit/920e83219911df1c440d3fe42fd5ec3a295b0bb8)) + - Update changelogs prior to release ([`b3e2252`](https://github.com/Byron/gitoxide/commit/b3e2252f7461a003d9a4612da60ba931dd8c0bef)) + * **[#250](https://github.com/Byron/gitoxide/issues/250)** + - Move loose header manipulation from git-pack to git-object ([`598698b`](https://github.com/Byron/gitoxide/commit/598698b88c194bc0e6ef69539f9fa7246ebfab70)) + * **[#254](https://github.com/Byron/gitoxide/issues/254)** + - Adjust changelogs prior to git-pack release ([`6776a3f`](https://github.com/Byron/gitoxide/commit/6776a3ff9fa5a283da06c9ec5723d13023a0b267)) + * **[#259](https://github.com/Byron/gitoxide/issues/259)** + - Btree/hashmap free lookup of packs in store, keeping things more bundled ([`a88981b`](https://github.com/Byron/gitoxide/commit/a88981b6f38b86624588f0c8ff200d17f38d0263)) + * **[#260](https://github.com/Byron/gitoxide/issues/260)** + - Linked::Store now assures unique IDs across compound stores ([`b605c1f`](https://github.com/Byron/gitoxide/commit/b605c1fa0494b10872d3c2e6ecce0e39f1a90a9e)) + * **[#266](https://github.com/Byron/gitoxide/issues/266)** + - More explicit information about how much garbaged is in the slotmap ([`cfd36ee`](https://github.com/Byron/gitoxide/commit/cfd36ee60172a13d147dea06c9ad5db76501053a)) + - Assure stable handles can actually access the indices hey need ([`9474a43`](https://github.com/Byron/gitoxide/commit/9474a439dd884d4d7a54e87beab212a0b0ced030)) + - A failing test to show the handle-stability doesn't quite work yet ([`5562e88`](https://github.com/Byron/gitoxide/commit/5562e8888cd8ac8fc3d89a41f8e8cc5cec7b8ca6)) + - Refactor ([`c499843`](https://github.com/Byron/gitoxide/commit/c499843485a8af102cb4d3594c4e6014976c5aa0)) + - Docs for dynamic object store ([`2c2a2e9`](https://github.com/Byron/gitoxide/commit/2c2a2e9d69e917983518ee91c66c96e90d34850e)) + - Default handle refresh mode is the least surprising, with option to configure ([`1b74c14`](https://github.com/Byron/gitoxide/commit/1b74c14c99a3076753f166dc1a6a4451bca490d2)) + - Remove unused dependencies ([`c800fdd`](https://github.com/Byron/gitoxide/commit/c800fdd331e6d7a0b8d756ba822915259f26e9e8)) + - Refactor ([`b88f253`](https://github.com/Byron/gitoxide/commit/b88f253e46e7ad0a50b670b96c1bfa09eaaecaef)) + - Refactor ([`52a4dcd`](https://github.com/Byron/gitoxide/commit/52a4dcd3a6969fa8f423ab39c875f98f9d210e95)) + - Refactor ([`3da91ce`](https://github.com/Byron/gitoxide/commit/3da91ce78ae582989cc1da136a1884dfe21de2b3)) + - Move `sink::Sink` to the top-level exclusively ([`ab4e726`](https://github.com/Byron/gitoxide/commit/ab4e726fcec65871a81056a9c69af8ea3f56b2a3)) + - Dynamic store module cleanu ([`494772c`](https://github.com/Byron/gitoxide/commit/494772cec65a5c29d0c45e4f130cc9a8ee2023b9)) + - Adapt to changes in git-odb ([`a44dd4b`](https://github.com/Byron/gitoxide/commit/a44dd4b5d1910856d7a21e156e7bca3138c04484)) + - Move `loose::iter::Iter` to `loose::Iter` ([`8bb5c9a`](https://github.com/Byron/gitoxide/commit/8bb5c9a75cd91ae0d888bc8e93707cfc9cc08090)) + - Minor improvements to module layout, docs ([`0364f48`](https://github.com/Byron/gitoxide/commit/0364f48ff4cd25bd4e73524651b68e65694bb5f7)) + - Fix docs ([`360bf9d`](https://github.com/Byron/gitoxide/commit/360bf9d759e724b164a38f8f59423bf0b63d2d0c)) + - Make single-threaded programs possible to use with git-repository ([`dde5c6b`](https://github.com/Byron/gitoxide/commit/dde5c6ba76ff849f69f742c985b4bc65ca830883)) + - A more suitable iterator implementation for general store ([`af0cc5f`](https://github.com/Byron/gitoxide/commit/af0cc5f27488df497bc929492bb13386aa88cc2a)) + - A quick and dirty version index iteration ([`0384007`](https://github.com/Byron/gitoxide/commit/0384007cd9e813cf4bfb13642adef8a602d219ad)) + - Use new store in git-repository ([`2f9e342`](https://github.com/Byron/gitoxide/commit/2f9e342b63f9e5c925d8e85ebc0a0be693ca0901)) + - Use new odb in place of the old one and it works ([`8ad25c5`](https://github.com/Byron/gitoxide/commit/8ad25c581bc79041545a72baf57b0a469d99cc30)) + - Remaining methods of git-pack::Find ([`92b9764`](https://github.com/Byron/gitoxide/commit/92b97644e9828ab84727135d42fdab5101340a3f)) + - Make find::Entry self-contained ([`ad36fb9`](https://github.com/Byron/gitoxide/commit/ad36fb9b800c17931ce358ac262bef40d43dcfb3)) + - Remove iterator access in favor of fully owned data ([`62d3f10`](https://github.com/Byron/gitoxide/commit/62d3f106437e597a41aae592da28f48e8736b143)) + - It shows that we can't return anything referenced from the interior-mutable handle ([`b9f308b`](https://github.com/Byron/gitoxide/commit/b9f308b6f3e5961b7ebeffbbc412cc9398fa0e2a)) + - Refactor ([`6cb474e`](https://github.com/Byron/gitoxide/commit/6cb474ec2e50e0a7cc5bfaff49a1e866279e8269)) + - Prepare implementation of location-dependent methods ([`5de29f4`](https://github.com/Byron/gitoxide/commit/5de29f4440bb0258bf2262ee210e1a511b74a4d3)) + - Refactor ([`2c23f42`](https://github.com/Byron/gitoxide/commit/2c23f42dc8b965aca5043652355aecb978084fa5)) + - Impl git_odb::Write for general::Handle ([`b7a6ab7`](https://github.com/Byron/gitoxide/commit/b7a6ab71788643039313193869e96b4e9364d7ba)) + - Cleanup ([`a4f3670`](https://github.com/Byron/gitoxide/commit/a4f36704e0243555c3d021f2925fbb15c396a026)) + - A way to predict the amount of slots needed for smooth operation ([`a3a16d6`](https://github.com/Byron/gitoxide/commit/a3a16d6abfb245654353aaf1df0c9d932b250bf3)) + - Finding objects and dynamically loading packs seems to work ([`8f58c30`](https://github.com/Byron/gitoxide/commit/8f58c30e3a6251b318514e468fc03d743d85e4f3)) + - First sketch of looking up objects with pack cache ([`a0aae84`](https://github.com/Byron/gitoxide/commit/a0aae843d32bc0ebf672e5fe268e4fd1fd1fc51e)) + - Add contains checks for libgit2 ([`c64a45a`](https://github.com/Byron/gitoxide/commit/c64a45a6453d72227260839cd93b8e8fa54d7357)) + - Add MRU to contains() even faster contains checks ([`6525847`](https://github.com/Byron/gitoxide/commit/65258476dfc4ce35c30dbb48066f510f3de08073)) + - Load an index right after refreshing items from disk ([`0c40eb3`](https://github.com/Byron/gitoxide/commit/0c40eb3f73c10eb58f7704e2a3f1fb3171bc4300)) + - Some more assertions for contains() regarding refresh mode ([`3f8c540`](https://github.com/Byron/gitoxide/commit/3f8c540625a14c33f26eece6f1b89997db5abab9)) + - Try reusing mappings, but no LRU ([`bb602e8`](https://github.com/Byron/gitoxide/commit/bb602e8ab22bfb48043e26d2ecfd5380056582c5)) + - Try LRU-like contains implementation ([`2d6960f`](https://github.com/Byron/gitoxide/commit/2d6960f886c1165f0bdb6f2d653388e1e0b57a2d)) + - Don't abort run into assertion unnecessarily… ([`4e87a56`](https://github.com/Byron/gitoxide/commit/4e87a56cef4644e592d93999e064adbc436e3cec)) + - Adjust object-acess to test new contains method ([`8488b41`](https://github.com/Byron/gitoxide/commit/8488b41651751d9177f53a23233b7ddd655dd696)) + - Looks like 'contains()' is implemented well enough ([`c24015a`](https://github.com/Byron/gitoxide/commit/c24015a076963ed0feceadf9269c52f7910ad033)) + - First successful loading of indices… ([`8cbef57`](https://github.com/Byron/gitoxide/commit/8cbef57aa2ad82591521dd9ff7e529e3171f6153)) + - First stab at loading indices while dealing with inherent raciness ([`94be3a0`](https://github.com/Byron/gitoxide/commit/94be3a0f3e51a3377e8a263e4e3b638c9b64cf0e)) + - Assure loops can't happen anymore ([`f04ff80`](https://github.com/Byron/gitoxide/commit/f04ff8011198b7f6c45c2094530903316c6e91ea)) + - The first green test for loose object lookup ([`0c6b7b1`](https://github.com/Byron/gitoxide/commit/0c6b7b13dd532350d649d932e93ff016170a1860)) + - More clarity around generations; actually trash slots or unset them ([`7bce101`](https://github.com/Byron/gitoxide/commit/7bce101ac4e085b3d035069f51ea99f945961990)) + - Create a new index snapshot and store it ([`41d91f9`](https://github.com/Byron/gitoxide/commit/41d91f96cfc6ca41902a268537b6865f7df867fa)) + - Sort out race condition around slots that change identity ([`6e678e7`](https://github.com/Byron/gitoxide/commit/6e678e705172c988de2a849352b4b83f898eeaa4)) + - Uncover slightly disturbing races which make it hard to ever release/unload maps ([`cbf2d13`](https://github.com/Byron/gitoxide/commit/cbf2d13afaee11cd835872c60063bac885829751)) + - Remaining comments about multi-pack-index handling ([`569c40b`](https://github.com/Byron/gitoxide/commit/569c40b7b8c468901266e24c4b8dbde0d0365912)) + - Handling of multi-pack index slot-map moves as they change ([`456f1e7`](https://github.com/Byron/gitoxide/commit/456f1e7f1bb72a28c31aa3ff80e488cd81888900)) + - Prepare correct handling of multi-pack indices when the time comes ([`6388ba2`](https://github.com/Byron/gitoxide/commit/6388ba2f82adced3f3f268f248063b9cb5a6d269)) + - More thoughts about how to continue a search… ([`eac8c45`](https://github.com/Byron/gitoxide/commit/eac8c45ae5961cc6a19d803b589c5f816fddcc5e)) + - Get closer to actually setting up slots ([`55645ae`](https://github.com/Byron/gitoxide/commit/55645ae8a9d3f7666f5c6f9d28302b7afeb355ed)) + - Add load-pack method frame to not forget ([`e1ec535`](https://github.com/Byron/gitoxide/commit/e1ec535ed16010c7d8d4dec659d0b3d6ab8863e5)) + - Some steps towards recording the disk state in the slot-map ([`5074f4c`](https://github.com/Byron/gitoxide/commit/5074f4ce711e443cc456ddb7e6ce978610331ded)) + - Better errors for disk consolidation; setup loose object dbs ([`4c13e14`](https://github.com/Byron/gitoxide/commit/4c13e149bdd111cf7ddb171881c8ac95c88f3808)) + - Detail the load-indices flow more ([`11d3325`](https://github.com/Byron/gitoxide/commit/11d3325dec84b50dab41ed12ba52a1b939ac3003)) + - Make slot-count configurable ([`c910af5`](https://github.com/Byron/gitoxide/commit/c910af59806aa65e2b56a81357fd3b21e877791c)) + - Provide handle with a snapshot of the store's state ([`6e0cd6d`](https://github.com/Byron/gitoxide/commit/6e0cd6d38c5df874990ace6c2c3c0b39342c4d05)) + - Refactor ([`d5565da`](https://github.com/Byron/gitoxide/commit/d5565daf9ece760af99e6b6c7c711440b95cd359)) + - Support for metrics in general store handle ([`11b98b8`](https://github.com/Byron/gitoxide/commit/11b98b8d2a0ecafaf6b57efafa3ed8ac40b00298)) + - First test to trigger all major code-paths ([`25b56c5`](https://github.com/Byron/gitoxide/commit/25b56c5257230db296453a2ac74f73e694d0af77)) + - More trustworthy state-id hashing ([`4eb43d0`](https://github.com/Byron/gitoxide/commit/4eb43d03e4954fa6d4ab4afb5f13ad5e801ba58a)) + - Use handle registration to avoid unloading packs; fix state-id hash ([`a1070de`](https://github.com/Byron/gitoxide/commit/a1070de8b750acbbaf14925d827574465ea69804)) + - Handle registration ([`df4e4eb`](https://github.com/Byron/gitoxide/commit/df4e4ebd116d0d80e6a8b09bb5ea30ecfdbfbae9)) + - Bring in the slotmap ([`3a5cb5f`](https://github.com/Byron/gitoxide/commit/3a5cb5ffd326dba8e1f8d6ca725c4d2815864a9b)) + - Put down more types for loading of indices and refresh logic ([`9909eaf`](https://github.com/Byron/gitoxide/commit/9909eaf363e8681454ea1dd2b953fe8757c23463)) + - Add all types the handle would have to store ([`e2f0cb0`](https://github.com/Byron/gitoxide/commit/e2f0cb07b9c23360ee3cc68b7093fc742ecef16d)) + - Rename `Handle` to `Cache` ([`580e96c`](https://github.com/Byron/gitoxide/commit/580e96c1b2d9782a2e8cf9d1123f6d53a5376a3d)) + - First sketch of general store ([`fc1b640`](https://github.com/Byron/gitoxide/commit/fc1b6409380256b73cf271c105802f4494dbb8c5)) + - More affirmative notes about multi-pack indices ([`dceaea2`](https://github.com/Byron/gitoxide/commit/dceaea2486afcfce053eac1ad41851cddce9c5e7)) + - Remove CRC32 check entirely as it doesn't seem to be important in the big picture ([`22d35bd`](https://github.com/Byron/gitoxide/commit/22d35bdbc271ccada8d68a1450d9a2533fc739ee)) + - Notes about multi-pack indices in the current data::entry::location ([`7eff6bf`](https://github.com/Byron/gitoxide/commit/7eff6bf525ea48fa913149911ea4c8fe742a25a3)) + - Adjust to new name/place of `bundle::Location` ([`1f8954d`](https://github.com/Byron/gitoxide/commit/1f8954d7b7567990055fe7d1752599c6a0a90931)) + - Add docs for handle-related functions ([`cf1b1e6`](https://github.com/Byron/gitoxide/commit/cf1b1e6d82f691ab17975e4f1479d93720368803)) + - Impl Write for Arc, Rc and shared borrows ([`5cdc27d`](https://github.com/Byron/gitoxide/commit/5cdc27df2f5a25668f5c8bce4cbe1dcb0262ccc1)) + - Adapt to changes in git-repository ([`3ab9b03`](https://github.com/Byron/gitoxide/commit/3ab9b03eee7d449b7bb87cb7dcbf164fdbe4ca48)) + - Also use object cache if there is no pack-cache ([`3e1ae25`](https://github.com/Byron/gitoxide/commit/3e1ae256d380eca1cb66f9348287bcf9bdfcefca)) + - Remove `make_object_cache` parameter from `git_pack::data::output::count::objects()` ([`3f05fea`](https://github.com/Byron/gitoxide/commit/3f05fea55dc8acce1ed62ecbe4e0a1394f2720b7)) + - Cache-creators are indeed shared across threads, must be sync ([`c326cb3`](https://github.com/Byron/gitoxide/commit/c326cb35cc684a5751e007c0ece3f02edf162ecc)) + - Try to make Handle usable for pack creation ([`424c9b3`](https://github.com/Byron/gitoxide/commit/424c9b3a2b467f5a1e339700257cd4ab72e2e692)) + - Make all handle caches and cache creators optional ([`3c30769`](https://github.com/Byron/gitoxide/commit/3c307694708532a9903ce7ff7138d0d4ab17dba5)) + - Make odb handle more general ([`2be6725`](https://github.com/Byron/gitoxide/commit/2be6725b76b5f4a10dbadc73b2a7dfc3c1537223)) + - :Find for Arc and Rc ([`b1c82a7`](https://github.com/Byron/gitoxide/commit/b1c82a7959fba1541642fc8dfae46b27848f2ba3)) + - MultiPackIndex compatible pack::Find trait definition ([`5fa1a9d`](https://github.com/Byron/gitoxide/commit/5fa1a9dce59c2654374a532d024c8de5959d4d0f)) + - Git_pack::Find::try_find_cached(…, pack_cache) ([`58c2edb`](https://github.com/Byron/gitoxide/commit/58c2edb76755ab71e10eef4cd9a51533825c291f)) + - Construct a handle from a linked store ([`9702ed4`](https://github.com/Byron/gitoxide/commit/9702ed492114a64c8dab0fedc3e251c9e059a753)) + - Refactor ([`ab14f70`](https://github.com/Byron/gitoxide/commit/ab14f70202a5a80369a640eff809003925bccb58)) + - A first tiny stab at Handle for linked store ([`ef08f7f`](https://github.com/Byron/gitoxide/commit/ef08f7fd186dca5ff0d02e8b57c8d14e6b584e65)) + - Refactor ([`1361c31`](https://github.com/Byron/gitoxide/commit/1361c31d43438cb14723003194236fe7d1fdc201)) + - Refactor ([`3310d8f`](https://github.com/Byron/gitoxide/commit/3310d8f271f74fc6084e33dd9bd4c5f01b54e432)) + - :Find implementation for linked::Store ([`9235106`](https://github.com/Byron/gitoxide/commit/9235106986e14551a28693bfe4ea92f046c65406)) + - Add linked::Store::rc_iter() ([`36fde72`](https://github.com/Byron/gitoxide/commit/36fde720c34e02429a810ddd43b894a37516f51a)) + - Use `Deref` instead of Borrow in linked ODB iterator ([`a96b1e5`](https://github.com/Byron/gitoxide/commit/a96b1e5e626f58bc4d79d44132b4f03bd3a7cfd1)) + - Remove pack-cache from `Find::try_find(…)` ([`ebc7f47`](https://github.com/Byron/gitoxide/commit/ebc7f47708a63c3df4415ba0e702660d976dfb3e)) + - Linked::Store sorts bundles by modification date, newest first ([`a81b333`](https://github.com/Byron/gitoxide/commit/a81b33359a4394a66f854195445f8f9aa0a46179)) + - Move git_pack::data::Object to git_object::Data, massively alter git_odb::Find trait ([`2290d00`](https://github.com/Byron/gitoxide/commit/2290d006705ff47ad780b009fe58ee422b3285af)) + - A simplified version of the `Find` trait ([`e25f4ea`](https://github.com/Byron/gitoxide/commit/e25f4eadec679406aad6df10026e27e4832c2482)) + - Add 'contains()' method to Find ([`dfdd6fb`](https://github.com/Byron/gitoxide/commit/dfdd6fb2c83e5d09c3a56936723bc6749ac4b99a)) + * **[#279](https://github.com/Byron/gitoxide/issues/279)** + - Add missing docs ([`4137327`](https://github.com/Byron/gitoxide/commit/41373274fc7f23e3fed17dc52e3e3e94c2e9e41a)) + - Very first experimental support for multi-pack index verification ([`bb35c69`](https://github.com/Byron/gitoxide/commit/bb35c6994765ec3bbbcfde247911d1ffe711a23d)) + - Multi-index verify checksum ([`853d468`](https://github.com/Byron/gitoxide/commit/853d4683aae5f4dd4667b452932bd57f99f6afab)) + - Another test to validate index stability ([`1fb08df`](https://github.com/Byron/gitoxide/commit/1fb08dfb00e951c0851aaf8b0d9b64bcdf5a3e3e)) + - Refactor ([`a4ad5bf`](https://github.com/Byron/gitoxide/commit/a4ad5bf948f405bdf00847859a95aa50082e0615)) + - Handle 'move' of multi-pack indices ([`aec0895`](https://github.com/Byron/gitoxide/commit/aec0895c1d4a47727e530b1e80b5289db966ab91)) + - Trigger last portion of multi-index logic ([`b46979b`](https://github.com/Byron/gitoxide/commit/b46979b9aa57b718ed38acbaf9ca5cd7b775a6fc)) + - Load slots early to avoid races with the 'generation' field ([`10bc3ab`](https://github.com/Byron/gitoxide/commit/10bc3ab6c4407fa1fdb966d3fc880aa772c490d3)) + - Only open multi-pack indices if the mtime changed ([`efe2579`](https://github.com/Byron/gitoxide/commit/efe2579b70c078f42b65077461a63abe1f1b6950)) + - Add remaining Store docs ([`e440bcd`](https://github.com/Byron/gitoxide/commit/e440bcdd7525e7f79a889b55610f64ff00827a2c)) + - First odb lookup with multi-index works ([`a8773df`](https://github.com/Byron/gitoxide/commit/a8773dfd728a0e996a5d4853508e5560e3bbb03d)) + - Generalize intra-pack offset lookup ([`dff05ef`](https://github.com/Byron/gitoxide/commit/dff05ef0c242a05e234d441d044f9b294ebefe0c)) + - Replace bare u32 `data::Id` typedef ([`e0b8636`](https://github.com/Byron/gitoxide/commit/e0b8636f96e4bfe1bc72b5aa6ad4c4c8538ff92c)) + - A rough implementation of everything multi-index support would need… ([`56f174f`](https://github.com/Byron/gitoxide/commit/56f174f37e1a0fa671bc54be2a8d16878eab75a6)) + - Make opened multi-pack indices representable ([`28e648d`](https://github.com/Byron/gitoxide/commit/28e648d642c89422fdc23292582d601b79601262)) + - First sketch towards reading in multi-indices ([`25eb157`](https://github.com/Byron/gitoxide/commit/25eb157ccb9f02b404e66f0d953228cd56424436)) + - `Cache::inner` removed in favor of `Deref/Mut` and `into_inner()` ([`bf73a94`](https://github.com/Byron/gitoxide/commit/bf73a94b43288b6634dbb33f2433656987a73baf)) + - Cargo fmt ([`8b9da35`](https://github.com/Byron/gitoxide/commit/8b9da35b3e0d3458efcac150f7062c9d7382a6c4)) + - Change accessors named `hash_kind()` to `object_hash()` for consistency ([`2ef9a84`](https://github.com/Byron/gitoxide/commit/2ef9a8424af51310db8c1e6df31dde9953ed3d21)) + - Consistently use `object_hash` instead of `hash_kind` ([`49998cc`](https://github.com/Byron/gitoxide/commit/49998cce419a27f3928ec4ac39da5e3b500e5cb2)) + - Refactor ([`7331e99`](https://github.com/Byron/gitoxide/commit/7331e99cb88df19f7b1e04b1468584e9c7c79913)) + - Adapt to changes in git-pack ([`28dba20`](https://github.com/Byron/gitoxide/commit/28dba20d0ba6197d02e1c9b665279392dad8d707)) + - Adjust to changes in git-pack ([`b8f109e`](https://github.com/Byron/gitoxide/commit/b8f109efa3bf3b9f37033bddd841fdf55e450dd4)) + - Refactor ([`9b5451a`](https://github.com/Byron/gitoxide/commit/9b5451a071ea303216c750dc94f2f646fc3c366f)) + - Loose object iteration respects hash kind ([`72eb9da`](https://github.com/Byron/gitoxide/commit/72eb9da4e2aa543ea902c49e0477274bd631c7c3)) + - Remove `Write::*(…, hash_kind)` ([`67c42fb`](https://github.com/Byron/gitoxide/commit/67c42fbf5f88f8dc42a9ebd7c6276d57ba1d4624)) + - Various changes to the `loose::Store` ([`ad1b9ea`](https://github.com/Byron/gitoxide/commit/ad1b9ea17eb4b98ebd2fddebe82a8fee1d63e9dd)) + - Loose odb doesn't hard-code sha1 anymore for `find()` and `contains()` ([`68f1031`](https://github.com/Byron/gitoxide/commit/68f1031732cfb8f50fa582ced834f7d4fd9da2ae)) + - First pieces of header parsing; allow to respect multi-index desired hash kind in git-odb ([`1a2a049`](https://github.com/Byron/gitoxide/commit/1a2a04930ab56ba778091e10b15cecf415f5058d)) + - Respect `core.multiPackIndex` option ([`1495efc`](https://github.com/Byron/gitoxide/commit/1495efcc914449f9680f9141805d60b1f3188001)) + - Only load multi-pack indices if allowed ([`b22e146`](https://github.com/Byron/gitoxide/commit/b22e14663dd0237a0ee1f5283782953ec8442a16)) + * **[#287](https://github.com/Byron/gitoxide/issues/287)** + - Basic output for 'repo verify' json only ([`9f8d61f`](https://github.com/Byron/gitoxide/commit/9f8d61f164fb3fbdb76cc44fbd634ca5db35b3b8)) + - Way nicer progress messages for repo verification ([`4b4f9f8`](https://github.com/Byron/gitoxide/commit/4b4f9f81879ad181744022eb0d7dc02392a5e91e)) + - Upgrade to prodash 17 ([`47860b7`](https://github.com/Byron/gitoxide/commit/47860b7e2769260cfb8522ae455c491605093423)) + - Better verify progress printing ([`4a464f2`](https://github.com/Byron/gitoxide/commit/4a464f26bb4dae236bcc845e353e25dd5d936f8f)) + - Refactor ([`831397c`](https://github.com/Byron/gitoxide/commit/831397c99fee9f2d6758124d993386cca5534f7b)) + - Refactor ([`38426a1`](https://github.com/Byron/gitoxide/commit/38426a171844014201282a441ebfc7d1f4cfff94)) + - Very rough version of repository verification ([`80a4a7a`](https://github.com/Byron/gitoxide/commit/80a4a7add688d16376b9bf2ed7f1c7f655b7c912)) + - Support for loose object statistics in odb store ([`53d835a`](https://github.com/Byron/gitoxide/commit/53d835a4a8a2830c56a354a3e7f5c1790ee40ad1)) + - Bare-bones loose object integrity check ([`3dfec81`](https://github.com/Byron/gitoxide/commit/3dfec817e4980b19a3d0eb1301d8f33296b2fbcf)) + - Frame for loose-db validation ([`a24307d`](https://github.com/Byron/gitoxide/commit/a24307dfd0b7322472f85ec83687a04488d28cff)) + - First basic validation of all packs within an odb store ([`d63176f`](https://github.com/Byron/gitoxide/commit/d63176f2cf0a869285a7434311a8adfdfbf552c7)) + - Don't reset generations, instead make them match the current one ([`1d995ef`](https://github.com/Byron/gitoxide/commit/1d995ef672e7bfcb142a369ac034a802e02ad124)) + - Frame for interity check on object store ([`b5dd059`](https://github.com/Byron/gitoxide/commit/b5dd059343f0545c77129595f3801a946cfe8c5f)) + - Cleanup and unify `verify_integrity()` method signature ([`91d0476`](https://github.com/Byron/gitoxide/commit/91d047658b114f372735116c9d8e6962a3873137)) + * **[#293](https://github.com/Byron/gitoxide/issues/293)** + - Refactor ([`9b28b18`](https://github.com/Byron/gitoxide/commit/9b28b18262c763608d60fba65e91fcb9ca3ddb3e)) + * **[#298](https://github.com/Byron/gitoxide/issues/298)** + - Handle::packed_object_count() ([`84ec54e`](https://github.com/Byron/gitoxide/commit/84ec54e904378c5b3d7da9efff66b02e88b16916)) + - Docs ([`a45f378`](https://github.com/Byron/gitoxide/commit/a45f3789696078848e2e96ddb8a55570c941dd53)) + - Implement ODB::disambiguate_prefix(…) ([`7d4d281`](https://github.com/Byron/gitoxide/commit/7d4d2818395cfe0c31117f8736471d4a707e3feb)) + - Add missing docs ([`c7cd4ab`](https://github.com/Byron/gitoxide/commit/c7cd4ab9a2ba2674be2dcd97a16f549e14679149)) + - Refactor ([`4455af3`](https://github.com/Byron/gitoxide/commit/4455af376a583fe524855d9199faa0a374b2ab31)) + - Refactor ([`fe87704`](https://github.com/Byron/gitoxide/commit/fe87704d037ae4cef5d48abd7dfc152f37a43c97)) + - Lookup_prefix for ODB ([`a4ccd18`](https://github.com/Byron/gitoxide/commit/a4ccd18fb3662f9fa38f73693abe9364c77df3d3)) + - Base for testing git_odb::Handle::lookup_prefix() ([`6244c06`](https://github.com/Byron/gitoxide/commit/6244c06900a1c66b2d62bd12015aaae86ac1c842)) + - Basics for ODB lookup prefix ([`5c228e1`](https://github.com/Byron/gitoxide/commit/5c228e1596b0ace28a1261ca271c3f1d93eb3970)) + - Add docs to `loose::Store::lookup_prefix(…)` ([`9fa4817`](https://github.com/Byron/gitoxide/commit/9fa4817f24ad1d31e20da92e307c853cdae758c1)) + - Loose::Store::lookup_prefix(…) ([`996bfb3`](https://github.com/Byron/gitoxide/commit/996bfb3061fd9ee2cf38c93f39e0d4c7c6163386)) + - Simplify error of loose::Iter ([`622abd7`](https://github.com/Byron/gitoxide/commit/622abd756c4d950076974e91dbe1a144b61ca3b1)) + - Upgrade parking_lot and cargo_toml ([`f95c1a0`](https://github.com/Byron/gitoxide/commit/f95c1a0d9c19bcc6feb9b8739a09d86f9970a0e0)) + - Support Rust 1.52 ([`9c14de3`](https://github.com/Byron/gitoxide/commit/9c14de391a1a9f1055922164d1757c9aa9720807)) + * **[#301](https://github.com/Byron/gitoxide/issues/301)** + - Update changelogs prior to release ([`84cb256`](https://github.com/Byron/gitoxide/commit/84cb25614a5fcddff297c1713eba4efbb6ff1596)) + - Adapt to changes in git-path ([`cc2d810`](https://github.com/Byron/gitoxide/commit/cc2d81012d107da7a61bf4de5b28342dea5083b7)) + - Use `git-path` crate instead of `git_features::path` ([`47e607d`](https://github.com/Byron/gitoxide/commit/47e607dc256a43a3411406c645eb7ff04239dd3a)) + - Make fmt ([`50ff7aa`](https://github.com/Byron/gitoxide/commit/50ff7aa7fa86e5e2a94fb15aab86470532ac3f51)) + - Adapt to changes in git-quote ([`ba48629`](https://github.com/Byron/gitoxide/commit/ba486297114154c334e0c38cd883504608973f3c)) + - Use git-quote crate in git-odb alternate parsing ([`8e49aa6`](https://github.com/Byron/gitoxide/commit/8e49aa6090c1c361e3ddd44798754c44c179ab49)) + - All path-related tests are green ([`81d2bf2`](https://github.com/Byron/gitoxide/commit/81d2bf2ec5f571245d56eb853306d07ede3010a2)) + - Update git-odb changelog to include information about bugfix ([`055b117`](https://github.com/Byron/gitoxide/commit/055b1170b9a96c9c6067a588fcd9679b618d9530)) + - Fix the first race-condition around initialization in ODB ([`a891315`](https://github.com/Byron/gitoxide/commit/a89131517fd4805211c4037396d9411ee41363d1)) + - Conversions from Rc to arc for Handle ([`c19331e`](https://github.com/Byron/gitoxide/commit/c19331e001e587e4fca74f3e9fec28a7df922c0a)) + - Less restrictive ref-delta-base resolution ([`917480b`](https://github.com/Byron/gitoxide/commit/917480b6626363555ba818c8e1c4e18cb944aa40)) + - More safety around recursion and invariants when resolving ref-deltas ([`dddb4a5`](https://github.com/Byron/gitoxide/commit/dddb4a51f417ff84a53da64959ad668ab26ebd93)) + - Allow delta base objects to be out-of-pack in general odb ([`d4f1590`](https://github.com/Byron/gitoxide/commit/d4f1590a6afe25fbd6659002c420098c57e1824a)) + - Elaborate odb info and simple entries printing ([`0f65282`](https://github.com/Byron/gitoxide/commit/0f65282fd2719234f745473e33bd42637be5fd3b)) + - A first sketch of access odb information using a sub-command ([`89b628a`](https://github.com/Byron/gitoxide/commit/89b628ab5b833a34f0b426b3a399bb182e63f3f4)) + * **[#329](https://github.com/Byron/gitoxide/issues/329)** + - Document all features related to serde1 ([`72b97f2`](https://github.com/Byron/gitoxide/commit/72b97f2ae4dc7642b160f183c6d5df4502dc186f)) + * **[#331](https://github.com/Byron/gitoxide/issues/331)** + - Adjustments due to breaking changes in `git_path` ([`4420ae9`](https://github.com/Byron/gitoxide/commit/4420ae932d5b20a9662a6d36353a27111b5cd672)) + - Adapt to changes in git_features::path to deal with Result ([`bba4c68`](https://github.com/Byron/gitoxide/commit/bba4c680c627a418efbd25f14bd168df19b8dedd)) + * **[#333](https://github.com/Byron/gitoxide/issues/333)** + - Use git_features::path everywhere where there is a path conversion ([`2e1437c`](https://github.com/Byron/gitoxide/commit/2e1437cb0b5dc77f2317881767f71eaf9b009ebf)) + * **[#364](https://github.com/Byron/gitoxide/issues/364)** + - A statement about replacement objects ([`2d32f4d`](https://github.com/Byron/gitoxide/commit/2d32f4d77efc6861323ec7de827cfe8db2c3c7a1)) + - Implement object replacement ([`b16d5e9`](https://github.com/Byron/gitoxide/commit/b16d5e9a5dcc5d3ad8275a8c793df50bb112eb3e)) + - Add some precaution to avoid strange interactions with packs ([`b052a9a`](https://github.com/Byron/gitoxide/commit/b052a9a3e9127fd9a4029594ea9de6e436db03c6)) + - An API and a test for replacement configuration ([`f2d6db1`](https://github.com/Byron/gitoxide/commit/f2d6db16f89bc70f1d167975cbd88937c4d38cfb)) + - Initial test to assure we don't replace objects by default ([`6cb9ecc`](https://github.com/Byron/gitoxide/commit/6cb9ecc4ef2602606a90880880ea7deccaaa0729)) + * **[#384](https://github.com/Byron/gitoxide/issues/384)** + - No need to isolate archives by crate name ([`19d46f3`](https://github.com/Byron/gitoxide/commit/19d46f35440419b9911b6e2bca2cfc975865dce9)) + - Add archive files via git-lfs ([`7202a1c`](https://github.com/Byron/gitoxide/commit/7202a1c4734ad904c026ee3e4e2143c0461d51a2)) + * **[#427](https://github.com/Byron/gitoxide/issues/427)** + - Make fmt ([`4b320e7`](https://github.com/Byron/gitoxide/commit/4b320e773368ac5e8c38dd8a779ef3d6d2d024ec)) + - Add `Cache::has_object_cache()` and `Cache::has_pack_cache()` methods. ([`81e1a9d`](https://github.com/Byron/gitoxide/commit/81e1a9d38aac9e6dd0618266ff826593e038cce8)) + - Improve error messages when objects aren't found ([`41d4943`](https://github.com/Byron/gitoxide/commit/41d494365d281056c5e9466860db808bd85143e9)) + - Fix: incorrect desired object kind if retrieved object doesn't have the expected kind ([`87f974e`](https://github.com/Byron/gitoxide/commit/87f974eea2cf7c6e3405b2816d3ef2bd058fc3dc)) + - Assure index ambiguous object range can represent 'none found' ([`5ffe54f`](https://github.com/Byron/gitoxide/commit/5ffe54ff88f026139474658fb470742751126119)) + - Avoid allocating index entries in case of ambiguity by using a range ([`4db4754`](https://github.com/Byron/gitoxide/commit/4db47547fa405542efd38b475e3e430548b9d160)) + - Provide optional `candidates` for ambigious entries during `lookup_prefix()` ([`95210cb`](https://github.com/Byron/gitoxide/commit/95210cb2ba85f75148b4ef48ccea9d9f8a0a0114)) + - `loose::Db` and `Store` can return all candidate objects for a single prefix ([`92d8be1`](https://github.com/Byron/gitoxide/commit/92d8be1101a7e76e70cd90db6a943b9e31e20802)) + - The first successful disambiguation test ([`6bc6337`](https://github.com/Byron/gitoxide/commit/6bc6337037708243346afeee07ad24a02565894b)) + * **[#470](https://github.com/Byron/gitoxide/issues/470)** + - Update changelogs prior to release ([`caa7a1b`](https://github.com/Byron/gitoxide/commit/caa7a1bdef74d7d3166a7e38127a59f5ab3cfbdd)) + * **[#536](https://github.com/Byron/gitoxide/issues/536)** + - Try even harder to find the original index during recursive base object resolution. ([`bae3ea9`](https://github.com/Byron/gitoxide/commit/bae3ea954af560de871fa7d506146e70041b5fca)) + - Try to recreate multi-threaded panic, without success. ([`53b5086`](https://github.com/Byron/gitoxide/commit/53b50866bcf9785904f1d8f809375edae9cee6f1)) + - Only run single-threaded these when respective toggle is set (when multi-threading is requested) ([`730384d`](https://github.com/Byron/gitoxide/commit/730384d6c8908ab8b44e9b499f3f7d678143e8ed)) + * **[#59](https://github.com/Byron/gitoxide/issues/59)** + - Fix initializing pack bundles in compound db ([`5a48e08`](https://github.com/Byron/gitoxide/commit/5a48e085d49a191a85a9b043e34d844389c8342b)) + - Add failing test ([`d629339`](https://github.com/Byron/gitoxide/commit/d629339834479553ceef27c15e5115e820b875ee)) + - Move pack fixtures into place which resembles an actual object db ([`fb5cea4`](https://github.com/Byron/gitoxide/commit/fb5cea4b9a98997f105a6ccb9729371be994af3c)) + * **[#63](https://github.com/Byron/gitoxide/issues/63)** + - Impl == and != for common combinations of ObjectId/oid ([`2455178`](https://github.com/Byron/gitoxide/commit/24551781cee4fcf312567ca9270d54a95bc4d7ae)) + - Git-protocol uses `oid` type ([`3930a6f`](https://github.com/Byron/gitoxide/commit/3930a6ff508f5bb2249fb2c2f21e00b74fecda22)) + - Use new `oid` where possible in git-odb ([`68a709e`](https://github.com/Byron/gitoxide/commit/68a709e0337d4969138d30a5c25d60b7dbe51a73)) + - Refactor; better errors for invalid hash sizes ([`be84b36`](https://github.com/Byron/gitoxide/commit/be84b36129694a2e89d1b81d932f2eba23aedf54)) + - Make ObjectId/oid happen! ([`ca78d15`](https://github.com/Byron/gitoxide/commit/ca78d15373ec988d909be8f240baefe75555e077)) + - Remove all public exports of git-hash types in git-object ([`accf89d`](https://github.com/Byron/gitoxide/commit/accf89d25560e5ded6f44a1c4a898ee65d14f8f6)) + - Remove re-export of git_object::borrowed::Id ([`a3f2816`](https://github.com/Byron/gitoxide/commit/a3f28169c1268c1129852f279631d5a7f7540cdf)) + * **[#67](https://github.com/Byron/gitoxide/issues/67)** + - Assure pack-ids are actually unique, the simple way… ([`0509b4f`](https://github.com/Byron/gitoxide/commit/0509b4fb5a78a3e4bfcacbeb661d262f8592884a)) + - The very first version of complete pack writing ([`4d76d53`](https://github.com/Byron/gitoxide/commit/4d76d53aabb956ed7c8a45c883486ec5596bcaa3)) + - A sketch of the pack::generation function signature ([`21b0aab`](https://github.com/Byron/gitoxide/commit/21b0aab81e7304da964dbef90c806134073ccef3)) + * **[#691](https://github.com/Byron/gitoxide/issues/691)** + - Set `rust-version` to 1.64 ([`55066ce`](https://github.com/Byron/gitoxide/commit/55066ce5fd71209abb5d84da2998b903504584bb)) + * **[#724](https://github.com/Byron/gitoxide/issues/724)** + - Fix typo proper ([`ffc99b8`](https://github.com/Byron/gitoxide/commit/ffc99b8edaacdb20c940624549acb4a4ee7f8c66)) + * **[#XXX](https://github.com/Byron/gitoxide/issues/XXX)** + - Prepare changelogs prior to release ([`8c0bca3`](https://github.com/Byron/gitoxide/commit/8c0bca37ff9fbaadbe55561fb2b0d649980c95b1)) + * **Uncategorized** + - Release gix-pack v0.30.2, gix-odb v0.40.2, gix-packetline v0.14.2, gix-transport v0.25.4, gix-protocol v0.26.3, gix-revision v0.10.3, gix-refspec v0.7.2, gix-worktree v0.12.2, gix v0.36.0 ([`59e9fac`](https://github.com/Byron/gitoxide/commit/59e9fac67d1b353e124300435b55f6b5468d7deb)) + - Release gix-index v0.12.3, gix-mailmap v0.9.2, gix-chunk v0.4.1, gix-pack v0.30.2, gix-odb v0.40.2, gix-packetline v0.14.2, gix-transport v0.25.4, gix-protocol v0.26.3, gix-revision v0.10.3, gix-refspec v0.7.2, gix-worktree v0.12.2, gix v0.36.0 ([`48f5bd2`](https://github.com/Byron/gitoxide/commit/48f5bd2014fa3dda6fbd60d091065c5537f69453)) + - Release gix-credentials v0.9.1, gix-diff v0.26.1, gix-discover v0.13.0, gix-hashtable v0.1.1, gix-bitmap v0.2.1, gix-traverse v0.22.1, gix-index v0.12.3, gix-mailmap v0.9.2, gix-chunk v0.4.1, gix-pack v0.30.2, gix-odb v0.40.2, gix-packetline v0.14.2, gix-transport v0.25.4, gix-protocol v0.26.3, gix-revision v0.10.3, gix-refspec v0.7.2, gix-worktree v0.12.2, gix v0.36.0 ([`a5869e0`](https://github.com/Byron/gitoxide/commit/a5869e0b223406820bca836e3e3a7fae2bfd9b04)) + - Release gix-config v0.16.1, gix-command v0.2.3, gix-prompt v0.3.2, gix-url v0.13.2, gix-credentials v0.9.1, gix-diff v0.26.1, gix-discover v0.13.0, gix-hashtable v0.1.1, gix-bitmap v0.2.1, gix-traverse v0.22.1, gix-index v0.12.3, gix-mailmap v0.9.2, gix-chunk v0.4.1, gix-pack v0.30.2, gix-odb v0.40.2, gix-packetline v0.14.2, gix-transport v0.25.4, gix-protocol v0.26.3, gix-revision v0.10.3, gix-refspec v0.7.2, gix-worktree v0.12.2, gix v0.36.0 ([`41d57b9`](https://github.com/Byron/gitoxide/commit/41d57b98964094fc1528adb09f69ca824229bf25)) + - Release gix-attributes v0.8.2, gix-config-value v0.10.1, gix-tempfile v3.0.2, gix-lock v3.0.2, gix-validate v0.7.2, gix-object v0.26.1, gix-ref v0.24.0, gix-sec v0.6.2, gix-config v0.16.1, gix-command v0.2.3, gix-prompt v0.3.2, gix-url v0.13.2, gix-credentials v0.9.1, gix-diff v0.26.1, gix-discover v0.13.0, gix-hashtable v0.1.1, gix-bitmap v0.2.1, gix-traverse v0.22.1, gix-index v0.12.3, gix-mailmap v0.9.2, gix-chunk v0.4.1, gix-pack v0.30.2, gix-odb v0.40.2, gix-packetline v0.14.2, gix-transport v0.25.4, gix-protocol v0.26.3, gix-revision v0.10.3, gix-refspec v0.7.2, gix-worktree v0.12.2, gix v0.36.0 ([`e313112`](https://github.com/Byron/gitoxide/commit/e31311257bd138b52042dea5fc40c3abab7f269b)) + - Release gix-features v0.26.4, gix-actor v0.17.1, gix-glob v0.5.3, gix-path v0.7.1, gix-quote v0.4.1, gix-attributes v0.8.2, gix-config-value v0.10.1, gix-tempfile v3.0.2, gix-lock v3.0.2, gix-validate v0.7.2, gix-object v0.26.1, gix-ref v0.24.0, gix-sec v0.6.2, gix-config v0.16.1, gix-command v0.2.3, gix-prompt v0.3.2, gix-url v0.13.2, gix-credentials v0.9.1, gix-diff v0.26.1, gix-discover v0.13.0, gix-hashtable v0.1.1, gix-bitmap v0.2.1, gix-traverse v0.22.1, gix-index v0.12.3, gix-mailmap v0.9.2, gix-chunk v0.4.1, gix-pack v0.30.2, gix-odb v0.40.2, gix-packetline v0.14.2, gix-transport v0.25.4, gix-protocol v0.26.3, gix-revision v0.10.3, gix-refspec v0.7.2, gix-worktree v0.12.2, gix v0.36.0 ([`6efd0d3`](https://github.com/Byron/gitoxide/commit/6efd0d31fbeca31ab7319aa2ac97bb31dc4ce055)) + - Release gix-date v0.4.2, gix-hash v0.10.2, gix-features v0.26.4, gix-actor v0.17.1, gix-glob v0.5.3, gix-path v0.7.1, gix-quote v0.4.1, gix-attributes v0.8.2, gix-config-value v0.10.1, gix-tempfile v3.0.2, gix-lock v3.0.2, gix-validate v0.7.2, gix-object v0.26.1, gix-ref v0.24.0, gix-sec v0.6.2, gix-config v0.16.1, gix-command v0.2.3, gix-prompt v0.3.2, gix-url v0.13.2, gix-credentials v0.9.1, gix-diff v0.26.1, gix-discover v0.13.0, gix-hashtable v0.1.1, gix-bitmap v0.2.1, gix-traverse v0.22.1, gix-index v0.12.3, gix-mailmap v0.9.2, gix-chunk v0.4.1, gix-pack v0.30.2, gix-odb v0.40.2, gix-packetline v0.14.2, gix-transport v0.25.4, gix-protocol v0.26.3, gix-revision v0.10.3, gix-refspec v0.7.2, gix-worktree v0.12.2, gix v0.36.0 ([`6ccc88a`](https://github.com/Byron/gitoxide/commit/6ccc88a8e4a56973b1a358cf72dc012ee3c75d56)) + - Apparently some fixtures were changed, so here are the archived ([`e49aed9`](https://github.com/Byron/gitoxide/commit/e49aed9d1d264030c73e431e7cd291d27546927b)) + - Merge branch 'rename-crates' into inform-about-gix-rename ([`c9275b9`](https://github.com/Byron/gitoxide/commit/c9275b99ea43949306d93775d9d78c98fb86cfb1)) + - Rename `git-testtools` to `gix-testtools` ([`b65c33d`](https://github.com/Byron/gitoxide/commit/b65c33d256cfed65d11adeff41132e3e58754089)) + - Adjust to renaming of `git-pack` to `gix-pack` ([`1ee81ad`](https://github.com/Byron/gitoxide/commit/1ee81ad310285ee4aa118118a2be3810dbace574)) + - Adjust to renaming of `git-odb` to `gix-odb` ([`476e2ad`](https://github.com/Byron/gitoxide/commit/476e2ad1a64e9e3f0d7c8651d5bcbee36cd78241)) + - Rename `git-odb` to `gix-odb` ([`93047bb`](https://github.com/Byron/gitoxide/commit/93047bb8bbb09982cd23ae6d78a0a90a9ec5c2a9)) + - Adjust to renaming of `git-index` to `gix-index` ([`86db5e0`](https://github.com/Byron/gitoxide/commit/86db5e09fc58ce66b252dc13b8d7e2c48e4d5062)) + - Adjust to renaming of `git-diff` to `gix-diff` ([`49a163e`](https://github.com/Byron/gitoxide/commit/49a163ec8b18f0e5fcd05a315de16d5d8be7650e)) + - Adjust to renaming of `git-commitgraph` to `gix-commitgraph` ([`f1dd0a3`](https://github.com/Byron/gitoxide/commit/f1dd0a3366e31259af029da73228e8af2f414244)) + - Adjust to renaming of `git-mailmap` to `gix-mailmap` ([`2e28c56`](https://github.com/Byron/gitoxide/commit/2e28c56bb9f70de6f97439818118d3a25859698f)) + - Adjust to renaming of `git-discover` to `gix-discover` ([`53adfe1`](https://github.com/Byron/gitoxide/commit/53adfe1c34e9ea3b27067a97b5e7ac80b351c441)) + - Adjust to renaming of `git-lfs` to `gix-lfs` ([`b9225c8`](https://github.com/Byron/gitoxide/commit/b9225c830daf1388484ee7e05f727990fdeff43c)) + - Adjust to renaming of `git-chunk` to `gix-chunk` ([`59194e3`](https://github.com/Byron/gitoxide/commit/59194e3a07853eae0624ebc4907478d1de4f7599)) + - Adjust to renaming of `git-bitmap` to `gix-bitmap` ([`75f2a07`](https://github.com/Byron/gitoxide/commit/75f2a079b17489f62bc43e1f1d932307375c4f9d)) + - Adjust to renaming for `git-protocol` to `gix-protocol` ([`823795a`](https://github.com/Byron/gitoxide/commit/823795addea3810243cab7936cd8ec0137cbc224)) + - Adjust to renaming of `git-refspec` to `gix-refspec` ([`c958802`](https://github.com/Byron/gitoxide/commit/c9588020561577736faa065e7e5b5bb486ca8fe1)) + - Adjust to renaming of `git-revision` to `gix-revision` ([`ee0ee84`](https://github.com/Byron/gitoxide/commit/ee0ee84607c2ffe11ee75f27a31903db68afed02)) + - Adjust to renaming of `git-transport` to `gix-transport` ([`b2ccf71`](https://github.com/Byron/gitoxide/commit/b2ccf716dc4425bb96651d4d58806a3cc2da219e)) + - Adjust to renaming of `git-credentials` to `gix-credentials` ([`6b18abc`](https://github.com/Byron/gitoxide/commit/6b18abcf2856f02ab938d535a65e51ac282bf94a)) + - Adjust to renaming of `git-prompt` to `gix-prompt` ([`6a4654e`](https://github.com/Byron/gitoxide/commit/6a4654e0d10ab773dd219cb4b731c0fc1471c36d)) + - Adjust to renaming of `git-command` to `gix-command` ([`d26b8e0`](https://github.com/Byron/gitoxide/commit/d26b8e046496894ae06b0bbfdba77196976cd975)) + - Adjust to renaming of `git-packetline` to `gix-packetline` ([`5cbd22c`](https://github.com/Byron/gitoxide/commit/5cbd22cf42efb760058561c6c3bbcd4dab8c8be1)) + - Adjust to renaming of `git-worktree` to `gix-worktree` ([`73a1282`](https://github.com/Byron/gitoxide/commit/73a12821b3d9b66ec1714d07dd27eb7a73e3a544)) + - Adjust to renamining of `git-hashtable` to `gix-hashtable` ([`26a0c98`](https://github.com/Byron/gitoxide/commit/26a0c98d0a389b03e3dc7bfc758b37155e285244)) + - Adjust to renamining of `git-worktree` to `gix-worktree` ([`108bb1a`](https://github.com/Byron/gitoxide/commit/108bb1a634f4828853fb590e9fc125f79441dd38)) + - Adjust to renaming of `git-url` to `gix-url` ([`b50817a`](https://github.com/Byron/gitoxide/commit/b50817aadb143e19f61f64e19b19ec1107d980c6)) + - Adjust to renaming of `git-date` to `gix-date` ([`9a79ff2`](https://github.com/Byron/gitoxide/commit/9a79ff2d5cc74c1efad9f41e21095ae498cce00b)) + - Adjust to renamining of `git-attributes` to `gix-attributes` ([`4a8b3b8`](https://github.com/Byron/gitoxide/commit/4a8b3b812ac26f2a2aee8ce8ca81591273383c84)) + - Adjust to renaminig of `git-quote` to `gix-quote` ([`648025b`](https://github.com/Byron/gitoxide/commit/648025b7ca94411fdd0d90c53e5faede5fde6c8d)) + - Adjust to renaming of `git-config` to `gix-config` ([`3a861c8`](https://github.com/Byron/gitoxide/commit/3a861c8f049f6502d3bcbdac752659aa1aeda46a)) + - Adjust to renaming of `git-ref` to `gix-ref` ([`1f5f695`](https://github.com/Byron/gitoxide/commit/1f5f695407b034377d94b172465ff573562b3fc3)) + - Adjust to renaming of `git-lock` to `gix-lock` ([`2028e78`](https://github.com/Byron/gitoxide/commit/2028e7884ae1821edeec81612f501e88e4722b17)) + - Adjust to renaming of `git-tempfile` to `gix-tempfile` ([`b6cc3eb`](https://github.com/Byron/gitoxide/commit/b6cc3ebb5137084a6327af16a7d9364d8f092cc9)) + - Adjust to renaming of `git-object` to `gix-object` ([`fc86a1e`](https://github.com/Byron/gitoxide/commit/fc86a1e710ad7bf076c25cc6f028ddcf1a5a4311)) + - Adjust to renaming of `git-actor` to `gix-actor` ([`4dc9b44`](https://github.com/Byron/gitoxide/commit/4dc9b44dc52f2486ffa2040585c6897c1bf55df4)) + - Adjust to renaming of `git-validate` to `gix-validate` ([`5e40ad0`](https://github.com/Byron/gitoxide/commit/5e40ad078af3d08cbc2ca81ce755c0ed8a065b4f)) + - Adjust to renaming of `git-hash` to `gix-hash` ([`4a9d025`](https://github.com/Byron/gitoxide/commit/4a9d0257110c3efa61d08c8457c4545b200226d1)) + - Adjust to renaming of `git-features` to `gix-features` ([`e2dd68a`](https://github.com/Byron/gitoxide/commit/e2dd68a417aad229e194ff20dbbfd77668096ec6)) + - Adjust to renaming of `git-glob` to `gix-glob` ([`35b2a3a`](https://github.com/Byron/gitoxide/commit/35b2a3acbc8f2a03f151bc0a3863163844e0ca86)) + - Adjust to renaming of `git-sec` to `gix-sec` ([`eabbb92`](https://github.com/Byron/gitoxide/commit/eabbb923bd5a32fc80fa80f96cfdc2ab7bb2ed17)) + - Adapt to renaming of `git-path` to `gix-path` ([`d3bbcfc`](https://github.com/Byron/gitoxide/commit/d3bbcfccad80fc44ea8e7bf819f23adaca06ba2d)) + - Adjust to rename of `git-config-value` to `gix-config-value` ([`622b3e1`](https://github.com/Byron/gitoxide/commit/622b3e1d0bffa0f8db73697960f9712024fac430)) + - Release git-features v0.26.4 ([`109f434`](https://github.com/Byron/gitoxide/commit/109f434e66559a791d541f86876ded8df10766f1)) + - Release git-features v0.26.3 ([`1ecfb7f`](https://github.com/Byron/gitoxide/commit/1ecfb7f8bfb24432690d8f31367488f2e59a642a)) + - Release git-date v0.4.2, git-hash v0.10.2, git-features v0.26.2, git-actor v0.17.1, git-glob v0.5.3, git-path v0.7.1, git-quote v0.4.1, git-attributes v0.8.2, git-config-value v0.10.1, git-tempfile v3.0.2, git-lock v3.0.2, git-validate v0.7.2, git-object v0.26.1, git-ref v0.24.0, git-sec v0.6.2, git-config v0.16.0, git-command v0.2.3, git-prompt v0.3.2, git-url v0.13.2, git-credentials v0.9.1, git-diff v0.26.1, git-discover v0.13.0, git-hashtable v0.1.1, git-bitmap v0.2.1, git-traverse v0.22.1, git-index v0.12.3, git-mailmap v0.9.2, git-chunk v0.4.1, git-pack v0.30.2, git-odb v0.40.2, git-packetline v0.14.2, git-transport v0.25.4, git-protocol v0.26.3, git-revision v0.10.2, git-refspec v0.7.2, git-worktree v0.12.2, git-repository v0.34.0, safety bump 3 crates ([`c196d20`](https://github.com/Byron/gitoxide/commit/c196d206d57a310b1ce974a1cf0e7e6d6db5c4d6)) + - Prepare changelogs prior to release ([`7c846d2`](https://github.com/Byron/gitoxide/commit/7c846d2102dc767366771925212712ef8cc9bf07)) + - Merge branch 'Lioness100/main' ([`1e544e8`](https://github.com/Byron/gitoxide/commit/1e544e82455bf9ecb5e3c2146280eaf7ecd81f16)) + - Undo typo-fix which reversed the meaning of the word. ([`eb232ff`](https://github.com/Byron/gitoxide/commit/eb232ffa7c6c939eb8286e07eed295d585afbe37)) + - Fix typos ([`39ed9ed`](https://github.com/Byron/gitoxide/commit/39ed9eda62b7718d5109135e5ad406fb1fe2978c)) + - Thanks clippy ([`bac57dd`](https://github.com/Byron/gitoxide/commit/bac57dd05ea2d5a4ee45ef9350fa3f2e19474bc0)) + - Make fmt ([`e22080e`](https://github.com/Byron/gitoxide/commit/e22080e4a29d0bad15a99d565a5e3e304a8743ec)) + - Merge branch 'adjustments-for-cargo' ([`7bba270`](https://github.com/Byron/gitoxide/commit/7bba2709488b7eb999b8136dbab03af977241678)) + - Break cyclical dev dependencies ([`1fea18f`](https://github.com/Byron/gitoxide/commit/1fea18f5f8b4189a23dc4fa3f041a672f6fbcfb3)) + - Use enumerations to advertise progress ids publicly. ([`d792ea5`](https://github.com/Byron/gitoxide/commit/d792ea543246632bf1ca8d0e1d239bbe7f07e219)) + - Release git-date v0.4.1, git-features v0.26.1, git-glob v0.5.2, git-attributes v0.8.1, git-tempfile v3.0.1, git-ref v0.23.1, git-sec v0.6.1, git-config v0.15.1, git-prompt v0.3.1, git-url v0.13.1, git-discover v0.12.1, git-index v0.12.2, git-mailmap v0.9.1, git-pack v0.30.1, git-odb v0.40.1, git-transport v0.25.3, git-protocol v0.26.2, git-revision v0.10.1, git-refspec v0.7.1, git-worktree v0.12.1, git-repository v0.33.0 ([`5b5b380`](https://github.com/Byron/gitoxide/commit/5b5b3809faa71c658db38b40dfc410224d08a367)) + - Prepare changelogs prior to release ([`93bef97`](https://github.com/Byron/gitoxide/commit/93bef97b3c0c75d4bf7119fdd787516e1efc77bf)) + - Merge branch 'patch-1' ([`b93f0c4`](https://github.com/Byron/gitoxide/commit/b93f0c49fc677b6c19aea332cbfc1445ce475375)) + - Thanks clippy ([`9e04685`](https://github.com/Byron/gitoxide/commit/9e04685dd3f109bfb27663f9dc7c04102e660bf2)) + - Release git-ref v0.23.0, git-config v0.15.0, git-command v0.2.2, git-diff v0.26.0, git-discover v0.12.0, git-mailmap v0.9.0, git-pack v0.30.0, git-odb v0.40.0, git-transport v0.25.2, git-protocol v0.26.1, git-revision v0.10.0, git-refspec v0.7.0, git-worktree v0.12.0, git-repository v0.32.0 ([`ffb5b6a`](https://github.com/Byron/gitoxide/commit/ffb5b6a21cb415315db6fd5294940c7c6deb4538)) + - Prepare changelogs prior to release ([`4381a03`](https://github.com/Byron/gitoxide/commit/4381a03a34c305f31713cce234c2afbf8ac60f01)) + - Release git-date v0.4.0, git-actor v0.17.0, git-object v0.26.0, git-traverse v0.22.0, git-index v0.12.0, safety bump 15 crates ([`0e3d0a5`](https://github.com/Byron/gitoxide/commit/0e3d0a56d7e6a60c6578138f2690b4fa54a2072d)) + - Release git-features v0.26.0, git-actor v0.16.0, git-attributes v0.8.0, git-object v0.25.0, git-ref v0.22.0, git-config v0.14.0, git-command v0.2.1, git-url v0.13.0, git-credentials v0.9.0, git-diff v0.25.0, git-discover v0.11.0, git-traverse v0.21.0, git-index v0.11.0, git-mailmap v0.8.0, git-pack v0.29.0, git-odb v0.39.0, git-transport v0.25.0, git-protocol v0.26.0, git-revision v0.9.0, git-refspec v0.6.0, git-worktree v0.11.0, git-repository v0.31.0, safety bump 24 crates ([`5ac9fbe`](https://github.com/Byron/gitoxide/commit/5ac9fbe265a5b61c533a2a6b3abfed2bdf7f89ad)) + - Prepare changelogs prior to release ([`30d8ca1`](https://github.com/Byron/gitoxide/commit/30d8ca19284049dcfbb0de2698cafae1d1a16b0c)) + - Make fmt ([`511ed00`](https://github.com/Byron/gitoxide/commit/511ed0000397a5b268530c8f5362e7d25b7c1594)) + - Release git-features v0.25.1, git-url v0.12.2, git-odb v0.38.1, git-transport v0.24.2, git-repository v0.30.2 ([`bb0a07b`](https://github.com/Byron/gitoxide/commit/bb0a07b5edd5f980989d1a92e74df7f183febe87)) + - Add test to verify we don't panic on a corrupt loose object ([`391adeb`](https://github.com/Byron/gitoxide/commit/391adeb69a73310baa3f08afa2c0a9aea7cfaf7c)) + - Merge branch 'loose-find-panic' ([`95cccdd`](https://github.com/Byron/gitoxide/commit/95cccddd3c181eb2a85b12823c27beb054adf5d8)) + - Flatten errors into one ([`46636e6`](https://github.com/Byron/gitoxide/commit/46636e64c9a48ec0e85e014ac0cc8b48846d8462)) + - Refactor ([`d305a3a`](https://github.com/Byron/gitoxide/commit/d305a3a8af77e7857c3f9b9866d103960ec024e1)) + - Remove panic-assertions in `loose` `lookup_prefix` ([`4fffa9a`](https://github.com/Byron/gitoxide/commit/4fffa9a9198cf3012fa8215796aab3d456519ff3)) + - Release git-date v0.3.1, git-features v0.25.0, git-actor v0.15.0, git-glob v0.5.1, git-path v0.7.0, git-attributes v0.7.0, git-config-value v0.10.0, git-lock v3.0.1, git-validate v0.7.1, git-object v0.24.0, git-ref v0.21.0, git-sec v0.6.0, git-config v0.13.0, git-prompt v0.3.0, git-url v0.12.0, git-credentials v0.8.0, git-diff v0.24.0, git-discover v0.10.0, git-traverse v0.20.0, git-index v0.10.0, git-mailmap v0.7.0, git-pack v0.28.0, git-odb v0.38.0, git-packetline v0.14.1, git-transport v0.24.0, git-protocol v0.25.0, git-revision v0.8.0, git-refspec v0.5.0, git-worktree v0.10.0, git-repository v0.30.0, safety bump 26 crates ([`e6b9906`](https://github.com/Byron/gitoxide/commit/e6b9906c486b11057936da16ed6e0ec450a0fb83)) + - Prepare chnagelogs prior to git-repository release ([`7114bbb`](https://github.com/Byron/gitoxide/commit/7114bbb6732aa8571d4ab74f28ed3e26e9fbe4d0)) + - Merge branch 'odb-iteration' ([`693a469`](https://github.com/Byron/gitoxide/commit/693a46977e2b57b93ee921320e008c8ad1beb81b)) + - Assure deltas are counted correctly, even if the base is out of pack. ([`ddaf47f`](https://github.com/Byron/gitoxide/commit/ddaf47f023970e8acfb98e8874da22f2604a92d9)) + - Support for pack-order when iterating objects. ([`e9d1f45`](https://github.com/Byron/gitoxide/commit/e9d1f45e944e91bb9715a3ee89a4f28b09250411)) + - Merge branch 'read-header' ([`3d01252`](https://github.com/Byron/gitoxide/commit/3d0125271ec7bd606734bd74757a7e31a18c7ce5)) + - Adjust to changes in `git-odb` ([`50ea7fb`](https://github.com/Byron/gitoxide/commit/50ea7fba30c752f86609fabf579a8a038b505c17)) + - Add `Store::try_header()` for obtaining object information quickly. ([`d9d05b0`](https://github.com/Byron/gitoxide/commit/d9d05b0db6b4453e7385117d466bf7c2e8de81fa)) + - Adapt to changes in `git-pack` ([`b1724ef`](https://github.com/Byron/gitoxide/commit/b1724efab49f6e656531e540b68315822ddafd22)) + - Add `loose::Store::try_header()` to obtain loose object information without content. ([`7f19bd7`](https://github.com/Byron/gitoxide/commit/7f19bd7e63d78e3151e43d5094ae9d35cbe34f46)) + - Merge branch 'main' into read-split-index ([`c57bdde`](https://github.com/Byron/gitoxide/commit/c57bdde6de37eca9672ea715962bbd02aa3eb055)) + - Merge branch 'adjustments-for-cargo' ([`083909b`](https://github.com/Byron/gitoxide/commit/083909bc7eb902eeee2002034fdb6ed88280dc5c)) + - Thanks clippy ([`f1160fb`](https://github.com/Byron/gitoxide/commit/f1160fb42acf59b37cbeda546a7079af3c9bc050)) + - Adapt to changes in `git-features::fs`. ([`35f7d59`](https://github.com/Byron/gitoxide/commit/35f7d5960210738d88d35aef9c1ed3480681c481)) + - Adjust to changes in `git-testtools` ([`4eb842c`](https://github.com/Byron/gitoxide/commit/4eb842c7150b980e1c2637217e1f9657a671cea7)) + - Merge branch 'adjustments-for-cargo' ([`70ccbb2`](https://github.com/Byron/gitoxide/commit/70ccbb21b1113bdeb20b52d274141a9fdb75f579)) + - Adapt to changes in `git-transport` ([`527c62e`](https://github.com/Byron/gitoxide/commit/527c62ef034a961a7e2b1dd1868cf8f81cc2eedc)) + - Release git-hash v0.10.1, git-hashtable v0.1.0 ([`7717170`](https://github.com/Byron/gitoxide/commit/771717095d9a67b0625021eb0928828ab686e772)) + - Merge branch 'main' into http-config ([`6b9632e`](https://github.com/Byron/gitoxide/commit/6b9632e16c416841ffff1b767ee7a6c89b421220)) + - Release git-features v0.24.1, git-actor v0.14.1, git-index v0.9.1 ([`7893502`](https://github.com/Byron/gitoxide/commit/789350208efc9d5fc6f9bc4f113f77f9cb445156)) + - Upgrade to `prodash 21.1` and add `Ids` to all progress instances. ([`c8835c6`](https://github.com/Byron/gitoxide/commit/c8835c6edae784c9ffcb69a674c0a6545dbb2af3)) + - Merge branch 'main' into http-config ([`bcd9654`](https://github.com/Byron/gitoxide/commit/bcd9654e56169799eb706646da6ee1f4ef2021a9)) + - Release git-hash v0.10.0, git-features v0.24.0, git-date v0.3.0, git-actor v0.14.0, git-glob v0.5.0, git-path v0.6.0, git-quote v0.4.0, git-attributes v0.6.0, git-config-value v0.9.0, git-tempfile v3.0.0, git-lock v3.0.0, git-validate v0.7.0, git-object v0.23.0, git-ref v0.20.0, git-sec v0.5.0, git-config v0.12.0, git-command v0.2.0, git-prompt v0.2.0, git-url v0.11.0, git-credentials v0.7.0, git-diff v0.23.0, git-discover v0.9.0, git-bitmap v0.2.0, git-traverse v0.19.0, git-index v0.9.0, git-mailmap v0.6.0, git-chunk v0.4.0, git-pack v0.27.0, git-odb v0.37.0, git-packetline v0.14.0, git-transport v0.23.0, git-protocol v0.24.0, git-revision v0.7.0, git-refspec v0.4.0, git-worktree v0.9.0, git-repository v0.29.0, git-commitgraph v0.11.0, gitoxide-core v0.21.0, gitoxide v0.19.0, safety bump 28 crates ([`b2c301e`](https://github.com/Byron/gitoxide/commit/b2c301ef131ffe1871314e19f387cf10a8d2ac16)) + - Prepare changelogs prior to release ([`e4648f8`](https://github.com/Byron/gitoxide/commit/e4648f827c97e9d13636d1bbdc83dd63436e6e5c)) + - Merge branch 'cwd-consistency' ([`ea7c6a3`](https://github.com/Byron/gitoxide/commit/ea7c6a3b069c9e13905b51b87538c57ba9182dca)) + - `alternate::resolve(…)` now takes the current_dir as argument. ([`1fabdc5`](https://github.com/Byron/gitoxide/commit/1fabdc51b9468ba2c6b8cf74509ad5aa2a0b86f4)) + - Merge branch 'jpgrayson/main' ([`b242853`](https://github.com/Byron/gitoxide/commit/b242853abd790e5234b2f18b4aaeddb8f6f4d36f)) + - Remove `git config` statements from fixtures that didn't need them (anymore). ([`578ea79`](https://github.com/Byron/gitoxide/commit/578ea799e9ec10f7142a7fc207d43ef301308c6d)) + - Disable tag.gpgSign in test scripts ([`1ce3190`](https://github.com/Byron/gitoxide/commit/1ce3190000f6211ce31468c7603d491bb5b90293)) + - Merge branch 'version2021' ([`0e4462d`](https://github.com/Byron/gitoxide/commit/0e4462df7a5166fe85c23a779462cdca8ee013e8)) + - Upgrade edition to 2021 in most crates. ([`3d8fa8f`](https://github.com/Byron/gitoxide/commit/3d8fa8fef9800b1576beab8a5bc39b821157a5ed)) + - Merge branch 'main' into http-config ([`7c5b37d`](https://github.com/Byron/gitoxide/commit/7c5b37d28e98f59a6847368a0d0166d2dbb4acc1)) + - Release git-diff v0.22.0, git-index v0.7.1, git-pack v0.26.0, git-odb v0.36.0, git-transport v0.21.2, git-repository v0.27.0, safety bump 6 crates ([`f0cab31`](https://github.com/Byron/gitoxide/commit/f0cab317bb0c2799fa80d16f3ae1b89d6aee4284)) + - Prepare changelogs prior to release ([`f5f3a9e`](https://github.com/Byron/gitoxide/commit/f5f3a9edd038a89c8c6c4da02054e5439bcc0071)) + - Release git-features v0.23.1, git-glob v0.4.1, git-config-value v0.8.1, git-tempfile v2.0.6, git-object v0.22.1, git-ref v0.18.0, git-sec v0.4.2, git-config v0.10.0, git-prompt v0.1.1, git-url v0.10.1, git-credentials v0.6.1, git-diff v0.21.0, git-discover v0.7.0, git-index v0.7.0, git-pack v0.25.0, git-odb v0.35.0, git-transport v0.21.1, git-protocol v0.22.0, git-refspec v0.3.1, git-worktree v0.7.0, git-repository v0.26.0, git-commitgraph v0.10.0, gitoxide-core v0.19.0, gitoxide v0.17.0, safety bump 9 crates ([`d071583`](https://github.com/Byron/gitoxide/commit/d071583c5576fdf5f7717765ffed5681792aa81f)) + - Prepare changelogs prior to release ([`423af90`](https://github.com/Byron/gitoxide/commit/423af90c8202d62dc1ea4a76a0df6421d1f0aa06)) + - Merge branch 'main' into write-sparse-index (upgrade to Rust 1.65) ([`5406630`](https://github.com/Byron/gitoxide/commit/5406630466145990b5adbdadb59151036993060d)) + - Thanks clippy ([`04cfa63`](https://github.com/Byron/gitoxide/commit/04cfa635a65ae34ad6d22391f2febd2ca7eabca9)) + - Adjust memory-size expectations to deal with Rust 1.65 and below ([`a93c470`](https://github.com/Byron/gitoxide/commit/a93c4703699ea61a646c82b861c9345715a6c057)) + - Release git-hash v0.9.11, git-features v0.23.0, git-actor v0.13.0, git-attributes v0.5.0, git-object v0.22.0, git-ref v0.17.0, git-sec v0.4.1, git-config v0.9.0, git-url v0.10.0, git-credentials v0.6.0, git-diff v0.20.0, git-discover v0.6.0, git-traverse v0.18.0, git-index v0.6.0, git-mailmap v0.5.0, git-pack v0.24.0, git-odb v0.34.0, git-packetline v0.13.1, git-transport v0.21.0, git-protocol v0.21.0, git-revision v0.6.0, git-refspec v0.3.0, git-worktree v0.6.0, git-repository v0.25.0, safety bump 24 crates ([`104d922`](https://github.com/Byron/gitoxide/commit/104d922add61ab21c534c24ce8ed37cddf3e275a)) + - Prepare changelogs for release ([`d232567`](https://github.com/Byron/gitoxide/commit/d23256701a95284857dc8d1cb37c7c94cada973c)) + - Merge branch 'main' into fetch-pack ([`d686020`](https://github.com/Byron/gitoxide/commit/d6860205db847b8a474756e92578195e1022481c)) + - Thanks clippy ([`b9937ad`](https://github.com/Byron/gitoxide/commit/b9937adc2c31095dde63397be7d56f1ea559b0f7)) + - Merge branch 'fix-git-features' ([`82fd251`](https://github.com/Byron/gitoxide/commit/82fd251ac80d07bc9da8a4d36e517aa35580d188)) + - Merge branch 'fix-odb-race' ([`b862fc5`](https://github.com/Byron/gitoxide/commit/b862fc52dd2409e912c892c7f428a571f565b64a)) + - Merge branch 'diff' ([`25a7726`](https://github.com/Byron/gitoxide/commit/25a7726377fbe400ea3c4927d04e9dec99802b7b)) + - Release git-command v0.1.0, git-prompt v0.1.0, git-url v0.9.0, git-credentials v0.5.0, git-diff v0.19.0, git-mailmap v0.4.0, git-chunk v0.3.2, git-pack v0.23.0, git-odb v0.33.0, git-packetline v0.13.0, git-transport v0.20.0, git-protocol v0.20.0, git-revision v0.5.0, git-refspec v0.2.0, git-repository v0.24.0, git-commitgraph v0.9.0, gitoxide-core v0.18.0, gitoxide v0.16.0 ([`f5c36d8`](https://github.com/Byron/gitoxide/commit/f5c36d85755d1f0f503b77d9a565fad6aecf6728)) + - Release git-hash v0.9.10, git-features v0.22.5, git-date v0.2.0, git-actor v0.12.0, git-glob v0.4.0, git-path v0.5.0, git-quote v0.3.0, git-attributes v0.4.0, git-config-value v0.8.0, git-tempfile v2.0.5, git-validate v0.6.0, git-object v0.21.0, git-ref v0.16.0, git-sec v0.4.0, git-config v0.8.0, git-discover v0.5.0, git-traverse v0.17.0, git-index v0.5.0, git-worktree v0.5.0, git-testtools v0.9.0, git-command v0.1.0, git-prompt v0.1.0, git-url v0.9.0, git-credentials v0.5.0, git-diff v0.19.0, git-mailmap v0.4.0, git-chunk v0.3.2, git-pack v0.23.0, git-odb v0.33.0, git-packetline v0.13.0, git-transport v0.20.0, git-protocol v0.20.0, git-revision v0.5.0, git-refspec v0.2.0, git-repository v0.24.0, git-commitgraph v0.9.0, gitoxide-core v0.18.0, gitoxide v0.16.0, safety bump 28 crates ([`29a043b`](https://github.com/Byron/gitoxide/commit/29a043be6808a3e9199a9b26bd076fe843afe4f4)) + - Release git-features v0.22.6 ([`c9eda72`](https://github.com/Byron/gitoxide/commit/c9eda729d8f8bc266c7516c613d38acfb83a4743)) + - Merge branch 'filter-refs-by-spec' ([`5c05198`](https://github.com/Byron/gitoxide/commit/5c051986bd89590a9287d85d84c713d83dfab83a)) + - Merge branch 'main' into filter-refs-by-spec ([`9aa1d3d`](https://github.com/Byron/gitoxide/commit/9aa1d3dc46d4b1c76af257f573aff3aeef2d3fa8)) + - Release git-features v0.22.4, git-url v0.8.0, safety bump 4 crates ([`1d4600a`](https://github.com/Byron/gitoxide/commit/1d4600ae51475c2e225f96c16c41e2c4a2b3f2aa)) + - Merge branch 'main' into filter-refs-by-spec ([`1f6e5ab`](https://github.com/Byron/gitoxide/commit/1f6e5ab15f5fd8d23719b13e6aea59cd231ac0fe)) + - Merge branch 'fix-522' ([`5869e9f`](https://github.com/Byron/gitoxide/commit/5869e9ff2508d5a93c07635277af8764fcb57713)) + - Release git-hash v0.9.9 ([`da0716f`](https://github.com/Byron/gitoxide/commit/da0716f8c27b4f29cfff0e5ce7fcb3d7240f4aeb)) + - Merge branch 'main' into index-from-tree ([`bc64b96`](https://github.com/Byron/gitoxide/commit/bc64b96a2ec781c72d1d4daad38aa7fb8b74f99b)) + - Release git-path v0.4.2, git-config-value v0.7.0 ([`c48fb31`](https://github.com/Byron/gitoxide/commit/c48fb3107d29f9a06868b0c6de40567063a656d1)) + - Merge branch 'main' into filter-refs-by-spec ([`cef0b51`](https://github.com/Byron/gitoxide/commit/cef0b51ade2a3301fa09ede7a425aa1fe3527e78)) + - Release git-object v0.20.3, git-ref v0.15.4, git-config v0.7.1, git-diff v0.18.0, git-traverse v0.16.3, git-pack v0.22.0, git-odb v0.32.0, git-url v0.7.3, git-transport v0.19.3, git-protocol v0.19.1, git-refspec v0.1.1, git-repository v0.23.0, safety bump 6 crates ([`85a3bed`](https://github.com/Byron/gitoxide/commit/85a3bedd68d2e5f36592a2f691c977dc55298279)) + - Release git-features v0.22.3, git-revision v0.4.4 ([`c2660e2`](https://github.com/Byron/gitoxide/commit/c2660e2503323531ba02519eaa51124ee22fec51)) + - Merge branch 'main' into filter-refs-by-spec ([`cfa1440`](https://github.com/Byron/gitoxide/commit/cfa144031dbcac2707ab0cec012bc35e78f9c475)) + - Release git-date v0.0.5, git-hash v0.9.8, git-features v0.22.2, git-actor v0.11.3, git-glob v0.3.2, git-quote v0.2.1, git-attributes v0.3.2, git-tempfile v2.0.4, git-lock v2.1.1, git-validate v0.5.5, git-object v0.20.2, git-ref v0.15.2, git-sec v0.3.1, git-config v0.7.0, git-credentials v0.4.0, git-diff v0.17.2, git-discover v0.4.1, git-bitmap v0.1.2, git-index v0.4.2, git-mailmap v0.3.2, git-chunk v0.3.1, git-traverse v0.16.2, git-pack v0.21.2, git-odb v0.31.2, git-packetline v0.12.7, git-url v0.7.2, git-transport v0.19.2, git-protocol v0.19.0, git-revision v0.4.2, git-refspec v0.1.0, git-worktree v0.4.2, git-repository v0.22.0, safety bump 4 crates ([`4974eca`](https://github.com/Byron/gitoxide/commit/4974eca96d525d1ee4f8cad79bb713af7a18bf9d)) + - Release git-path v0.4.1 ([`5e82346`](https://github.com/Byron/gitoxide/commit/5e823462b3deb904f5d6154a7bf114cef1988224)) + - Merge branch 'main' into remote-ls-refs ([`e2ee3de`](https://github.com/Byron/gitoxide/commit/e2ee3ded97e5c449933712883535b30d151c7c78)) + - Merge branch 'docsrs-show-features' ([`31c2351`](https://github.com/Byron/gitoxide/commit/31c235140cad212d16a56195763fbddd971d87ce)) + - Use docsrs feature in code to show what is feature-gated automatically on docs.rs ([`b1c40b0`](https://github.com/Byron/gitoxide/commit/b1c40b0364ef092cd52d03b34f491b254816b18d)) + - Uniformize deny attributes ([`f7f136d`](https://github.com/Byron/gitoxide/commit/f7f136dbe4f86e7dee1d54835c420ec07c96cd78)) + - Pass --cfg docsrs when compiling for https://docs.rs ([`5176771`](https://github.com/Byron/gitoxide/commit/517677147f1c17304c62cf97a1dd09f232ebf5db)) + - Merge branch 'main' into remote-ls-refs ([`bd5f3e8`](https://github.com/Byron/gitoxide/commit/bd5f3e8db7e0bb4abfb7b0f79f585ab82c3a14ab)) + - Release git-date v0.0.3, git-actor v0.11.1, git-attributes v0.3.1, git-tempfile v2.0.3, git-object v0.20.1, git-ref v0.15.1, git-config v0.6.1, git-diff v0.17.1, git-discover v0.4.0, git-bitmap v0.1.1, git-index v0.4.1, git-mailmap v0.3.1, git-traverse v0.16.1, git-pack v0.21.1, git-odb v0.31.1, git-packetline v0.12.6, git-url v0.7.1, git-transport v0.19.1, git-protocol v0.18.1, git-revision v0.4.0, git-worktree v0.4.1, git-repository v0.21.0, safety bump 5 crates ([`c96473d`](https://github.com/Byron/gitoxide/commit/c96473dce21c3464aacbc0a62d520c1a33172611)) + - Prepare changelogs prior to reelase ([`c06ae1c`](https://github.com/Byron/gitoxide/commit/c06ae1c606b6af9c2a12021103d99c2810750d60)) + - Release git-hash v0.9.7, git-features v0.22.1 ([`232784a`](https://github.com/Byron/gitoxide/commit/232784a59ded3e8016e4257c7e146ad385cdd64a)) + - Merge branch 'main' into write-index-v2 ([`a938986`](https://github.com/Byron/gitoxide/commit/a938986877302c197d1aed087594c5605416fe5f)) + - Merge branch 'main' into remote-ls-refs ([`de61c4d`](https://github.com/Byron/gitoxide/commit/de61c4db7855d6925d66961f62ae3d12cc4acf78)) + - Thanks clippy ([`4bd747c`](https://github.com/Byron/gitoxide/commit/4bd747cb3e126fe5b1d540270cfbd731cffd42ef)) + - Merge branch 'rev-parse-delegate' ([`2f506c7`](https://github.com/Byron/gitoxide/commit/2f506c7c2988477b0f97d272a9ac9ed47b236457)) + - Merge pull request #2 from SidneyDouw/main ([`ce885ad`](https://github.com/Byron/gitoxide/commit/ce885ad4c3324c09c83751c32e014f246c748766)) + - Merge branch 'Byron:main' into main ([`9b9ea02`](https://github.com/Byron/gitoxide/commit/9b9ea0275f8ff5862f24cf5a4ca53bb1cd610709)) + - Merge branch 'main' into rev-parse-delegate ([`6da8250`](https://github.com/Byron/gitoxide/commit/6da82507588d3bc849217c11d9a1d398b67f2ed6)) + - Merge branch 'main' into pathspec ([`7b61506`](https://github.com/Byron/gitoxide/commit/7b615060712565f515515e35a3e8346278ad770c)) + - Merge branch 'kianmeng-fix-typos' ([`4e7b343`](https://github.com/Byron/gitoxide/commit/4e7b34349c0a01ad8686bbb4eb987e9338259d9c)) + - Fix typos ([`e9fcb70`](https://github.com/Byron/gitoxide/commit/e9fcb70e429edb2974afa3f58d181f3ef14c3da3)) + - Release git-config v0.6.0, git-credentials v0.3.0, git-diff v0.17.0, git-discover v0.3.0, git-index v0.4.0, git-mailmap v0.3.0, git-traverse v0.16.0, git-pack v0.21.0, git-odb v0.31.0, git-url v0.7.0, git-transport v0.19.0, git-protocol v0.18.0, git-revision v0.3.0, git-worktree v0.4.0, git-repository v0.20.0, git-commitgraph v0.8.0, gitoxide-core v0.15.0, gitoxide v0.13.0 ([`aa639d8`](https://github.com/Byron/gitoxide/commit/aa639d8c43f3098cc4a5b50614c5ae94a8156928)) + - Release git-hash v0.9.6, git-features v0.22.0, git-date v0.0.2, git-actor v0.11.0, git-glob v0.3.1, git-path v0.4.0, git-attributes v0.3.0, git-tempfile v2.0.2, git-object v0.20.0, git-ref v0.15.0, git-sec v0.3.0, git-config v0.6.0, git-credentials v0.3.0, git-diff v0.17.0, git-discover v0.3.0, git-index v0.4.0, git-mailmap v0.3.0, git-traverse v0.16.0, git-pack v0.21.0, git-odb v0.31.0, git-url v0.7.0, git-transport v0.19.0, git-protocol v0.18.0, git-revision v0.3.0, git-worktree v0.4.0, git-repository v0.20.0, git-commitgraph v0.8.0, gitoxide-core v0.15.0, gitoxide v0.13.0, safety bump 22 crates ([`4737b1e`](https://github.com/Byron/gitoxide/commit/4737b1eea1d4c9a8d5a69fb63ecac5aa5d378ae5)) + - Prepare changelog prior to release ([`3c50625`](https://github.com/Byron/gitoxide/commit/3c50625fa51350ec885b0f38ec9e92f9444df0f9)) + - Merge pull request #1 from Byron/main ([`085e76b`](https://github.com/Byron/gitoxide/commit/085e76b121291ed9bd324139105d2bd4117bedf8)) + - Make fmt ([`0700b09`](https://github.com/Byron/gitoxide/commit/0700b09d6828849fa2470df89af1f75a67bfb27d)) + - Assure document-features are available in all 'usable' and 'early' crates ([`238581c`](https://github.com/Byron/gitoxide/commit/238581cc46c7288691eed37dc7de5069e3d86721)) + - Merge branch 'main' into pathspec ([`89ea12b`](https://github.com/Byron/gitoxide/commit/89ea12b558bcc056b892193ee8fb44b8664b5da4)) + - Merge branch 'main' into cont_include_if ([`0e9df36`](https://github.com/Byron/gitoxide/commit/0e9df364c4cddf006b1de18b8d167319b7cc1186)) + - Use git_path::realpath in all places that allow it right now ([`229dc91`](https://github.com/Byron/gitoxide/commit/229dc917fc7d9241b85e5818260a6fbdd3a5daaa)) + - Merge branch 'main' into cont_include_if ([`41ea8ba`](https://github.com/Byron/gitoxide/commit/41ea8ba78e74f5c988148367386a1f4f304cb951)) + - Release git-path v0.3.0, safety bump 14 crates ([`400c9be`](https://github.com/Byron/gitoxide/commit/400c9bec49e4ec5351dc9357b246e7677a63ea35)) + - Release git-date v0.0.1, git-hash v0.9.5, git-features v0.21.1, git-actor v0.10.1, git-path v0.2.0, git-attributes v0.2.0, git-ref v0.14.0, git-sec v0.2.0, git-config v0.5.0, git-credentials v0.2.0, git-discover v0.2.0, git-pack v0.20.0, git-odb v0.30.0, git-url v0.6.0, git-transport v0.18.0, git-protocol v0.17.0, git-revision v0.2.1, git-worktree v0.3.0, git-repository v0.19.0, safety bump 13 crates ([`a417177`](https://github.com/Byron/gitoxide/commit/a41717712578f590f04a33d27adaa63171f25267)) + - Update changelogs prior to release ([`bb424f5`](https://github.com/Byron/gitoxide/commit/bb424f51068b8a8e762696890a55ab48900ab980)) + - Merge branch 'main' into SidneyDouw-pathspec ([`a22b1d8`](https://github.com/Byron/gitoxide/commit/a22b1d88a21311d44509018729c3ef1936cf052a)) + - Merge branch 'main' into git_includeif ([`598c853`](https://github.com/Byron/gitoxide/commit/598c853087fcf8f77299aa5b9803bcec705c0cd0)) + - Release git-ref v0.13.0, git-discover v0.1.0, git-index v0.3.0, git-mailmap v0.2.0, git-traverse v0.15.0, git-pack v0.19.0, git-odb v0.29.0, git-packetline v0.12.5, git-url v0.5.0, git-transport v0.17.0, git-protocol v0.16.0, git-revision v0.2.0, git-worktree v0.2.0, git-repository v0.17.0 ([`349c590`](https://github.com/Byron/gitoxide/commit/349c5904b0dac350838a896759d51576b66880a7)) + - Release git-hash v0.9.4, git-features v0.21.0, git-actor v0.10.0, git-glob v0.3.0, git-path v0.1.1, git-attributes v0.1.0, git-sec v0.1.0, git-config v0.3.0, git-credentials v0.1.0, git-validate v0.5.4, git-object v0.19.0, git-diff v0.16.0, git-lock v2.1.0, git-ref v0.13.0, git-discover v0.1.0, git-index v0.3.0, git-mailmap v0.2.0, git-traverse v0.15.0, git-pack v0.19.0, git-odb v0.29.0, git-packetline v0.12.5, git-url v0.5.0, git-transport v0.17.0, git-protocol v0.16.0, git-revision v0.2.0, git-worktree v0.2.0, git-repository v0.17.0, safety bump 20 crates ([`654cf39`](https://github.com/Byron/gitoxide/commit/654cf39c92d5aa4c8d542a6cadf13d4acef6a78e)) + - Merge branch 'main' into msrv-for-windows ([`7cb1972`](https://github.com/Byron/gitoxide/commit/7cb19729133325bdfacedf44cdc0500cbcf36684)) + - Merge branch 'worktree-stack' ([`98da8ba`](https://github.com/Byron/gitoxide/commit/98da8ba52cef8ec27f705fcbc84773e5bacc4e10)) + - Merge branch 'main' into repo-status ([`0eb2372`](https://github.com/Byron/gitoxide/commit/0eb23721dca78f6e6bf864c5c3a3e44df8b419f0)) + - Merge branch 'test-archive-support' ([`350df01`](https://github.com/Byron/gitoxide/commit/350df01042d6ca8b93f8737fa101e69b50535a0f)) + - Merge branch 'main' into repo-status ([`4086335`](https://github.com/Byron/gitoxide/commit/40863353a739ec971b49410fbc2ba048b2762732)) + - Merge branch 'worktree-stack' ([`e90d3fd`](https://github.com/Byron/gitoxide/commit/e90d3fd0a9764511e6280596f21d3a0494ed7021)) + - Release git-config v0.2.1, git-diff v0.15.0, git-traverse v0.14.0, git-pack v0.18.0, git-odb v0.28.0, git-ref v0.12.1, git-revision v0.1.0, git-repository v0.16.0, gitoxide-core v0.14.0, gitoxide v0.12.0, safety bump 6 crates ([`b612021`](https://github.com/Byron/gitoxide/commit/b612021683ba709b693bd48aef3e2e3c2f5b9ead)) + - Remove deprecated compound and linked object databases ([`8c5ae77`](https://github.com/Byron/gitoxide/commit/8c5ae77f06a64c57df9a9ad1190266896a223dbe)) + - Release git-diff v0.14.0, git-bitmap v0.1.0, git-index v0.2.0, git-tempfile v2.0.1, git-lock v2.0.0, git-mailmap v0.1.0, git-traverse v0.13.0, git-pack v0.17.0, git-quote v0.2.0, git-odb v0.27.0, git-packetline v0.12.4, git-url v0.4.0, git-transport v0.16.0, git-protocol v0.15.0, git-ref v0.12.0, git-worktree v0.1.0, git-repository v0.15.0, cargo-smart-release v0.9.0, safety bump 5 crates ([`e58dc30`](https://github.com/Byron/gitoxide/commit/e58dc3084cf17a9f618ae3a6554a7323e44428bf)) + - Merge branch 'for-onefetch' ([`8e5cb65`](https://github.com/Byron/gitoxide/commit/8e5cb65da75036a13ed469334e7ae6c527d9fff6)) + - Release git-hash v0.9.3, git-features v0.20.0, git-config v0.2.0, safety bump 12 crates ([`f0cbb24`](https://github.com/Byron/gitoxide/commit/f0cbb24b2e3d8f028be0e773f9da530da2656257)) + - Make fmt ([`7cf3545`](https://github.com/Byron/gitoxide/commit/7cf354509b545f7e7c99e159b5989ddfbe86273d)) + - Merge branch 'main' into mailmap ([`b2df941`](https://github.com/Byron/gitoxide/commit/b2df941feaf5ae9fa170fa49270189f3527f2eab)) + - Merge branch 'describe-rev' ([`77b7cd9`](https://github.com/Byron/gitoxide/commit/77b7cd9a7813aaa1a15d035ea42c1e3fe4eef8dd)) + - Adapt to breaking changes in git-actor ([`40c48c3`](https://github.com/Byron/gitoxide/commit/40c48c390eb796b427ebd516dde92e9538ce5fb7)) + - Merge branch 'short-id' ([`5849d5b`](https://github.com/Byron/gitoxide/commit/5849d5b326b83f98a16cf1d956c720c7f0fd4445)) + - Thanks clippy ([`4618f8a`](https://github.com/Byron/gitoxide/commit/4618f8aa7648c0553a8e1b023fceb6738654e38b)) + - Merge branch 'svetli-n-path_value' ([`e8383ca`](https://github.com/Byron/gitoxide/commit/e8383caf6db211beb57d70019fe4ad13ce9066ee)) + - Release git-tempfile v2.0.0, safety bump 6 crates ([`90b1c42`](https://github.com/Byron/gitoxide/commit/90b1c42d5487904a9f329362d185b035d0ddb975)) + - Merge branch 'unify-path-encoding' ([`566ff8a`](https://github.com/Byron/gitoxide/commit/566ff8a3597b889899d41ca15e5b9af7e05f1a4b)) + - Release git-hash v0.9.2, git-object v0.17.1, git-pack v0.16.1 ([`0db19b8`](https://github.com/Byron/gitoxide/commit/0db19b8deaf11a4d4cbc03fa3ae40eea104bc302)) + - Merge branch 'index-verification' ([`ad3c803`](https://github.com/Byron/gitoxide/commit/ad3c8032cee02052ef3940d1d7c950270a0a299a)) + - Implement `git_odb::Find` for `git_odb::Handle` ([`3522aef`](https://github.com/Byron/gitoxide/commit/3522aef37bcaa285e14d7a84cdca67321f9125bb)) + - Release git-odb v0.26.0, git-packetline v0.12.3, git-url v0.3.5, git-transport v0.15.0, git-protocol v0.14.0, git-ref v0.11.0, git-repository v0.14.0, cargo-smart-release v0.8.0 ([`42ebb53`](https://github.com/Byron/gitoxide/commit/42ebb536cd6086f096b8422291776c9720fa0948)) + - Release git-diff v0.13.0, git-tempfile v1.0.4, git-chunk v0.3.0, git-traverse v0.12.0, git-pack v0.16.0, git-odb v0.26.0, git-packetline v0.12.3, git-url v0.3.5, git-transport v0.15.0, git-protocol v0.14.0, git-ref v0.11.0, git-repository v0.14.0, cargo-smart-release v0.8.0 ([`1b76119`](https://github.com/Byron/gitoxide/commit/1b76119259b8168aeb99cbbec233f7ddaa2d7d2c)) + - Release git-actor v0.8.0, git-config v0.1.10, git-object v0.17.0, git-diff v0.13.0, git-tempfile v1.0.4, git-chunk v0.3.0, git-traverse v0.12.0, git-pack v0.16.0, git-odb v0.26.0, git-packetline v0.12.3, git-url v0.3.5, git-transport v0.15.0, git-protocol v0.14.0, git-ref v0.11.0, git-repository v0.14.0, cargo-smart-release v0.8.0 ([`8f57c29`](https://github.com/Byron/gitoxide/commit/8f57c297d7d6ed68cf51415ea7ede4bf9263326e)) + - Release git-features v0.19.1, git-actor v0.8.0, git-config v0.1.10, git-object v0.17.0, git-diff v0.13.0, git-tempfile v1.0.4, git-chunk v0.3.0, git-traverse v0.12.0, git-pack v0.16.0, git-odb v0.26.0, git-packetline v0.12.3, git-url v0.3.5, git-transport v0.15.0, git-protocol v0.14.0, git-ref v0.11.0, git-repository v0.14.0, cargo-smart-release v0.8.0 ([`d78aab7`](https://github.com/Byron/gitoxide/commit/d78aab7b9c4b431d437ac70a0ef96263acb64e46)) + - Release git-hash v0.9.1, git-features v0.19.1, git-actor v0.8.0, git-config v0.1.10, git-object v0.17.0, git-diff v0.13.0, git-tempfile v1.0.4, git-chunk v0.3.0, git-traverse v0.12.0, git-pack v0.16.0, git-odb v0.26.0, git-packetline v0.12.3, git-url v0.3.5, git-transport v0.15.0, git-protocol v0.14.0, git-ref v0.11.0, git-repository v0.14.0, cargo-smart-release v0.8.0, safety bump 4 crates ([`373cbc8`](https://github.com/Byron/gitoxide/commit/373cbc877f7ad60dac682e57c52a7b90f108ebe3)) + - Prepare changelogs for release ([`674ec73`](https://github.com/Byron/gitoxide/commit/674ec73b0816baa2c63b4ef1b40b7a41849c5e95)) + - Prepar changelogs for cargo-smart-release release ([`8900d69`](https://github.com/Byron/gitoxide/commit/8900d699226eb0995be70d66249827ce348261df)) + - Release git-bitmap v0.0.1, git-hash v0.9.0, git-features v0.19.0, git-index v0.1.0, safety bump 9 crates ([`4624725`](https://github.com/Byron/gitoxide/commit/4624725f54a34dd6b35d3632fb3516965922f60a)) + - Merge branch 'use-midx-in-store' ([`338521b`](https://github.com/Byron/gitoxide/commit/338521b0443b9dc1007581de42ef6a950f6e0bbf)) + - Release git-chunk v0.2.0, safety bump 4 crates ([`b792fab`](https://github.com/Byron/gitoxide/commit/b792fabf9f5f93ab906ac5a5bb3e4f01c179290a)) + - Refactor ([`c09a44d`](https://github.com/Byron/gitoxide/commit/c09a44db8e2bf6f45ebcd7423ab7438308557c49)) + - Merge branch 'sync-db-draft' ([`7d2e20c`](https://github.com/Byron/gitoxide/commit/7d2e20c6fedc2c7e71a307d8d072412fa847a4aa)) + - Thanks clippy ([`bf4694c`](https://github.com/Byron/gitoxide/commit/bf4694c895ac7e73f22e6424808269b91f17003f)) + - Thanks clippy ([`17af184`](https://github.com/Byron/gitoxide/commit/17af184b7e24e39ce78b6c07dbced5d530ef3d6d)) + - Thanks clippy ([`123a95e`](https://github.com/Byron/gitoxide/commit/123a95ed2a7eef30fc2071769d96469dc17b2195)) + - Thanks clippy ([`4ca9e07`](https://github.com/Byron/gitoxide/commit/4ca9e07c7ac062d48d64ad7b516274e32dbc51c6)) + - Make fmt ([`066f3ff`](https://github.com/Byron/gitoxide/commit/066f3ffb8740f242c1b03e680c3c5c1a0e4c36c3)) + - Release git-actor v0.7.0, git-config v0.1.9, git-object v0.16.0, git-diff v0.12.0, git-traverse v0.11.0, git-pack v0.15.0, git-odb v0.25.0, git-packetline v0.12.2, git-transport v0.14.0, git-protocol v0.13.0, git-ref v0.10.0, git-repository v0.13.0, cargo-smart-release v0.7.0 ([`d3f9227`](https://github.com/Byron/gitoxide/commit/d3f922781a81e8fbb81aa47afdbe9afeb06d666b)) + - Release git-features v0.18.0, git-actor v0.7.0, git-config v0.1.9, git-object v0.16.0, git-diff v0.12.0, git-traverse v0.11.0, git-pack v0.15.0, git-odb v0.25.0, git-packetline v0.12.2, git-transport v0.14.0, git-protocol v0.13.0, git-ref v0.10.0, git-repository v0.13.0, cargo-smart-release v0.7.0, safety bump 12 crates ([`acd3737`](https://github.com/Byron/gitoxide/commit/acd37371dcd92ebac3d1f039224d02f2b4e9fa0b)) + - Adjust changelogs prior to release ([`ec38950`](https://github.com/Byron/gitoxide/commit/ec3895005d141abe79764eaff7c0f04153e38d73)) + - Merge branch 'pack-consistency' ([`5982406`](https://github.com/Byron/gitoxide/commit/5982406b4e1b26fd383d9ec21a3cf652ec8ab25f)) + - Merge branch 'git-loose-objects' of https://github.com/xmo-odoo/gitoxide into xmo-odoo-git-loose-objects ([`ee737cd`](https://github.com/Byron/gitoxide/commit/ee737cd237ad70bf9f2c5e0d3e4557909e495bca)) + - Release git-config v0.1.8, git-object v0.15.1, git-diff v0.11.1, git-traverse v0.10.1, git-pack v0.14.0, git-odb v0.24.0, git-packetline v0.12.1, git-transport v0.13.1, git-protocol v0.12.1, git-ref v0.9.1, git-repository v0.12.0, cargo-smart-release v0.6.0 ([`f606fa9`](https://github.com/Byron/gitoxide/commit/f606fa9a0ca338534252df8921cd5e9d3875bf94)) + - Better changelog descriptions. ([`f69b2d6`](https://github.com/Byron/gitoxide/commit/f69b2d627099639bc144fd94fde678d84a10d6f7)) + - Adjusting changelogs prior to release of git-config v0.1.8, git-object v0.15.1, git-diff v0.11.1, git-traverse v0.10.1, git-pack v0.14.0, git-odb v0.24.0, git-packetline v0.12.1, git-transport v0.13.1, git-protocol v0.12.1, git-ref v0.9.1, git-repository v0.12.0, cargo-smart-release v0.6.0, safety bump 5 crates ([`39b40c8`](https://github.com/Byron/gitoxide/commit/39b40c8c3691029cc146b893fa0d8d25d56d0819)) + - Move "loose object header" ser/de to git-object ([`3d1565a`](https://github.com/Byron/gitoxide/commit/3d1565acfc336baf6487edccefd72d0226141a08)) + - Release git-hash v0.8.0, git-features v0.17.0, git-actor v0.6.0, git-object v0.15.0, git-diff v0.11.0, git-traverse v0.10.0, git-pack v0.13.0, git-odb v0.23.0, git-packetline v0.12.0, git-transport v0.13.0, git-protocol v0.12.0, git-ref v0.9.0, git-repository v0.11.0, git-commitgraph v0.6.0, gitoxide-core v0.12.0, gitoxide v0.10.0, cargo-smart-release v0.5.0, safety bump 16 crates ([`0e02953`](https://github.com/Byron/gitoxide/commit/0e029537a7f6242d02ccf7e63d8d92f5246e6c5e)) + - Release git-hash v0.7.0, git-features v0.16.5, git-actor v0.5.3, git-config v0.1.7, git-validate v0.5.3, git-object v0.14.1, git-diff v0.10.0, git-tempfile v1.0.3, git-lock v1.0.1, git-traverse v0.9.0, git-pack v0.12.0, git-odb v0.22.0, git-packetline v0.11.0, git-url v0.3.4, git-transport v0.12.0, git-protocol v0.11.0, git-ref v0.8.0, git-repository v0.10.0, cargo-smart-release v0.4.0 ([`59ffbd9`](https://github.com/Byron/gitoxide/commit/59ffbd9f15583c8248b7f48b3f55ec6faffe7cfe)) + - Adjusting changelogs prior to release of git-hash v0.7.0, git-features v0.16.5, git-actor v0.5.3, git-validate v0.5.3, git-object v0.14.1, git-diff v0.10.0, git-tempfile v1.0.3, git-lock v1.0.1, git-traverse v0.9.0, git-pack v0.12.0, git-odb v0.22.0, git-packetline v0.11.0, git-url v0.3.4, git-transport v0.12.0, git-protocol v0.11.0, git-ref v0.8.0, git-repository v0.10.0, cargo-smart-release v0.4.0, safety bump 3 crates ([`a474395`](https://github.com/Byron/gitoxide/commit/a47439590e36b1cb8b516b6053fd5cbfc42efed7)) + - Don't let 'a' leak out of the tempdir to fix #212 ([`682dff8`](https://github.com/Byron/gitoxide/commit/682dff83520fa7a09c88ce83fd1d6439c377a480)) + - More proper fix for #212 ([`dfc1493`](https://github.com/Byron/gitoxide/commit/dfc1493de348c166cd11eb6af3c145c994126472)) + - Naive fix for #212 ([`b31863c`](https://github.com/Byron/gitoxide/commit/b31863cc643833db5a7ac2edfe38b942dd15790c)) + - Update changelogs just for fun ([`21541b3`](https://github.com/Byron/gitoxide/commit/21541b3301de1e053fc0e84373be60d2162fbaae)) + - Merge branch 'changelog-generation' ([`bf0106e`](https://github.com/Byron/gitoxide/commit/bf0106ea21734d4e59d190b424c22743c22da966)) + - Bump git-traverse v0.9.0, safety bump 8 crates ([`d39fabb`](https://github.com/Byron/gitoxide/commit/d39fabb8757369aa19452a457f610fe21dc13a14)) + - Release git-odb v0.21.3 ([`223f930`](https://github.com/Byron/gitoxide/commit/223f93075a28dd49f44505c039cfeae5a7296914)) + - [smart-release #195] fix docs ([`8d7e132`](https://github.com/Byron/gitoxide/commit/8d7e132d055d8c87ea3e45de15593964a61b0608)) + - Release git-odb v0.21.2 ([`d443644`](https://github.com/Byron/gitoxide/commit/d44364445cfbae861ce45df8bddec1b34e03f454)) + - Bump git-pack v0.11.0 ([`5ae6ff5`](https://github.com/Byron/gitoxide/commit/5ae6ff52cd2cd1ccd1e26bb987c154eb19603696)) + - Bump git-object v0.14.0 ([`d4fc81f`](https://github.com/Byron/gitoxide/commit/d4fc81f6390443f8c8561d91ac27ea4a6318fb62)) + - [repository #164] Prepare `commit()` for a possible less-allocating future ([`0fd01f7`](https://github.com/Byron/gitoxide/commit/0fd01f7071c785c27c56d2c034aac8dcdf690677)) + - [repository #164] generic write_object() ([`c569f83`](https://github.com/Byron/gitoxide/commit/c569f83363489dde03c8b9cd01e75d35f5e04dbc)) + - [repository #164] Support for refreshing the object database ([`46e10f8`](https://github.com/Byron/gitoxide/commit/46e10f863e1fea419483a7b086022c16cd0ca226)) + - [odb #164] Add refresh() functionality ([`ee16d04`](https://github.com/Byron/gitoxide/commit/ee16d041941a5777c8f6495a28f7633c327cbd6b)) + - Release git-odb v0.21.1 ([`cb33c2f`](https://github.com/Byron/gitoxide/commit/cb33c2f71c2e1e228ba0d2299fb531cf2a5f3b23)) + - Merge branch 'repository-integration' ([`49f5453`](https://github.com/Byron/gitoxide/commit/49f5453629646ac24d752f53c532e5f67eb09374)) + - [odb #190] Read all eligble packed refs, no "pack-" prefix needed ([`ab250f7`](https://github.com/Byron/gitoxide/commit/ab250f76b356c0937ada959591dc4df3872acf8f)) + - Bump git-pack v0.10.0 ([`e5e3c80`](https://github.com/Byron/gitoxide/commit/e5e3c8024e1c2e5e90cee83abbdae41d58eee156)) + - Bump git-hash v0.6.0 ([`6efd90d`](https://github.com/Byron/gitoxide/commit/6efd90db54f7f7441b76159dba3be80c15657a3d)) + - [repository #174] adjust various changelogs ([`081faf5`](https://github.com/Byron/gitoxide/commit/081faf5c3a21b34b7068b44d8206fb5770c392f5)) + - [odb #180] fix docs ([`bd50752`](https://github.com/Byron/gitoxide/commit/bd50752dd9188acd92b8503db53cc2ce8112c61f)) + - [odb #180] refactor ([`eff21da`](https://github.com/Byron/gitoxide/commit/eff21dae1083042412f45cd2f7a0faaf7d6400e6)) + - Bump git-odb v0.21.0 ([`7b9854f`](https://github.com/Byron/gitoxide/commit/7b9854fb35e86958a5ca827ec9a55b1168f38395)) + - [odb #180] add changelog ([`acf1193`](https://github.com/Byron/gitoxide/commit/acf1193e6b72433d4b74ec9fd39de148529224c5)) + - [pack #179] refactor bundle ([`420dca2`](https://github.com/Byron/gitoxide/commit/420dca29bccca6e7d759880d8342f23b33eead0d)) + - [pack #179] refactor ([`ab6554b`](https://github.com/Byron/gitoxide/commit/ab6554b0cd5838f1ea4e82f6b5019798288076fa)) + - [object #177] move mutable objects to crate::* ([`c551c02`](https://github.com/Byron/gitoxide/commit/c551c0236c64f3237cb9be7f35159f753d4b871f)) + - [object #177] migrate immutable::tree to crate::tree ([`fa5cd06`](https://github.com/Byron/gitoxide/commit/fa5cd0648d5c855060ab2b75ee933851987c2dcf)) + - [object #177] move immutable::* to crate::*Ref, start `iter` adjustments ([`461dc53`](https://github.com/Byron/gitoxide/commit/461dc53ba3bc07d55fdb4aad7570ba9176a8b360)) + - [object #177] rename immutable::* to immutable::*Ref ([`6deb012`](https://github.com/Byron/gitoxide/commit/6deb01291fb382b7fb9206682e319afa81bacc05)) + - Release git-object v0.13.0 ([`708fc5a`](https://github.com/Byron/gitoxide/commit/708fc5abd8af4dd7459f388c7092bf35915c6662)) + - [actor #173] rename immutable::Signature to SignatureRef! ([`96461ac`](https://github.com/Byron/gitoxide/commit/96461ace776d6b351b313d4f2697f2d95b9e196e)) + - Release git-pack v0.9.0 ([`7fbc961`](https://github.com/Byron/gitoxide/commit/7fbc9617da97d4ba4bb3784f41d4163c0839c03c)) + - [pack #67] refactor ([`14717f6`](https://github.com/Byron/gitoxide/commit/14717f6132672a5d271832a68de0b323b73abb2a)) + - Release git-odb v0.20.2 ([`6fb8bbb`](https://github.com/Byron/gitoxide/commit/6fb8bbb11e87911424c95001ce851bc4665920e9)) + - Apply nightly rustfmt rules. ([`5e0edba`](https://github.com/Byron/gitoxide/commit/5e0edbadb39673d4de640f112fa306349fb11814)) + - Release git-odb v0.20.1 ([`ca3f736`](https://github.com/Byron/gitoxide/commit/ca3f736ae3e6a0a5541223364db874a8e31dd3ec)) + - Remove dev-dependency cycles by removing their version ([`c40faca`](https://github.com/Byron/gitoxide/commit/c40faca41632cd2a226daf4ddf5293b65d1fdc82)) + - Release git-diff v0.8.0, git-odb v0.20.0, git-pack v0.8.0, git-traverse v0.7.0 ([`f123f69`](https://github.com/Byron/gitoxide/commit/f123f69c7a4f9fd1c98bd2f60ebc953a6739fe04)) + - Release git-diff v0.7.0, git-odb v0.19.0, git-pack v0.7.0, git-traverse v0.6.0 ([`c67291f`](https://github.com/Byron/gitoxide/commit/c67291ff9bcdff9a747d87241f6a71015607af05)) + - Release git-object v0.12.0 ([`7006150`](https://github.com/Byron/gitoxide/commit/7006150ac314d19814608723f69f6e70a72f9262)) + - Release git-actor-0.3.1 ([`727087d`](https://github.com/Byron/gitoxide/commit/727087dca243da4bc40bc87611a2f66234565be7)) + - (cargo-release) version 0.18.0 ([`b327590`](https://github.com/Byron/gitoxide/commit/b327590d02fec5536c380b2d39dd7be089ca7c40)) + - (cargo-release) version 0.6.0 ([`d704bca`](https://github.com/Byron/gitoxide/commit/d704bca7de0a6591f35345c842d6418b36ecd206)) + - (cargo-release) version 0.17.0 ([`c52a491`](https://github.com/Byron/gitoxide/commit/c52a49176bd294bb36db74b4293cdb684a2ab7f6)) + - (cargo-release) version 0.5.0 ([`c2f94a5`](https://github.com/Byron/gitoxide/commit/c2f94a51bce287be301090450cb00cde57e92f76)) + - (cargo-release) version 0.4.0 ([`d69d0ac`](https://github.com/Byron/gitoxide/commit/d69d0ac21989243fdafa514fa41579fd51bc2558)) + - (cargo-release) version 0.11.0 ([`a5be31c`](https://github.com/Byron/gitoxide/commit/a5be31c4cf7c0b538a1ed4a52ff5c3a992c6feff)) + - (cargo-release) version 0.3.0 ([`64efc05`](https://github.com/Byron/gitoxide/commit/64efc0534ddc372b6e668b23c1e9d276098679c9)) + - (cargo-release) version 0.4.0 ([`70ef344`](https://github.com/Byron/gitoxide/commit/70ef3442775b54ba9e4ee9ebfffb37af9804cc5b)) + - [utils #154] refactor: bool.then(||this) - neat ([`1dec1c4`](https://github.com/Byron/gitoxide/commit/1dec1c49032c8acb449e463fde41f403cb640e45)) + - (cargo-release) version 0.16.1 ([`8cd173b`](https://github.com/Byron/gitoxide/commit/8cd173b32138a136e6109518c179bf7738fe6866)) + - (cargo-release) version 0.3.0 ([`0e9c73a`](https://github.com/Byron/gitoxide/commit/0e9c73abd17e0dd21952275077ae53ad7e7aa1af)) + - (cargo-release) version 0.5.0 ([`ae02dab`](https://github.com/Byron/gitoxide/commit/ae02dabae961089a92a21e6a60a7006de4b56dad)) + - (cargo-release) version 0.16.0 ([`1231dbd`](https://github.com/Byron/gitoxide/commit/1231dbd16dacefb39adec8e067c312d313a82e3c)) + - (cargo-release) version 0.2.0 ([`8ff5115`](https://github.com/Byron/gitoxide/commit/8ff511583e6d859e43ffda0ef75e2fecce3ed03c)) + - Clippy on tests and thanks clippy ([`a77a71c`](https://github.com/Byron/gitoxide/commit/a77a71cf02d328a2a964388928d6b2a235a0aa85)) + - Thanks clippy ([`e1964e4`](https://github.com/Byron/gitoxide/commit/e1964e43979b3e32a5d4bfbe377a842d2c0b10ea)) + - [pack] a way to obtain whole bundles for offset-to-index lookup ([`15fcbe2`](https://github.com/Byron/gitoxide/commit/15fcbe254b75e8f74652711cc339ae5ade74d24c)) + - [pack] refactor ([`64b1dcd`](https://github.com/Byron/gitoxide/commit/64b1dcdb0fb53749ce73017d0dc1e053689d17d4)) + - [pack] bundle::Location with pack offset; order counts by that… ([`f92f285`](https://github.com/Byron/gitoxide/commit/f92f285167c6b5bc4d86f255e360c4534e38bb29)) + - Don't use ASM on windows for Sha1 as it fails to build there. ([`ba1fb7a`](https://github.com/Byron/gitoxide/commit/ba1fb7ab5bc03f5a23ece32ff1e144544e1eaeae)) + - Remove unnecessary pub(crate) exports ([`3d2456e`](https://github.com/Byron/gitoxide/commit/3d2456e11709f0461b37c6df55ecc3861ca4cab5)) + - Bump thiserror from 1.0.25 to 1.0.26 ([`9682590`](https://github.com/Byron/gitoxide/commit/9682590095dc3a502b0c84ccd206ca4797635092)) + - [actor] fix gix hours ([`b4e95fd`](https://github.com/Byron/gitoxide/commit/b4e95fdbb6664adcb2603d9cb6e6a69182de050f)) + - [actor] git-object uses git-actor ([`d01dd2f`](https://github.com/Byron/gitoxide/commit/d01dd2f9e9e8e2b81cdb1131a436d32b5819b731)) + - Thanks clippy ([`3f7e27b`](https://github.com/Byron/gitoxide/commit/3f7e27b91e2c7d66959f5f4c1a667f3315111cd6)) + - (cargo-release) version 0.3.0 ([`6b33678`](https://github.com/Byron/gitoxide/commit/6b33678f83e6d261ca15c4a7634ff5b4e66d81dd)) + - (cargo-release) version 0.2.0 ([`3286e42`](https://github.com/Byron/gitoxide/commit/3286e42547b59df6365087cbae9ce1c9c959faad)) + - Thanks clippy ([`c5b4de8`](https://github.com/Byron/gitoxide/commit/c5b4de8c7675da47b5d6325d2993f4ebce4a8f0c)) + - [git-odb] linked::Store can now check if an object exists ([`bb95c79`](https://github.com/Byron/gitoxide/commit/bb95c7917a272bfe7eb04bea66685d6a1196dc25)) + - Refactor ([`a25a774`](https://github.com/Byron/gitoxide/commit/a25a774675e2e9db1c891351077d3af2fd5c72ed)) + - (cargo-release) version 0.4.0 ([`866f86f`](https://github.com/Byron/gitoxide/commit/866f86f59e66652968dcafc1a57912f9849cb21d)) + - [git-ref] the first failing test ([`7e802a0`](https://github.com/Byron/gitoxide/commit/7e802a0576230dfc666c253d484ea255f265f92f)) + - [git-odb] fix test compiilation ([`639bc10`](https://github.com/Byron/gitoxide/commit/639bc10e1698beb4c9e7902c2545dd0a3e90e698)) + - [git-odb] much better docs; cleanup exposed API ([`3d5b229`](https://github.com/Byron/gitoxide/commit/3d5b229c2605060f2cac9695ff2479777deabdd0)) + - (cargo-release) version 0.2.0 ([`b213628`](https://github.com/Byron/gitoxide/commit/b213628feeb8dfa87dab489c7d3155a60e6a236d)) + - [git-odb] refactor ([`2958145`](https://github.com/Byron/gitoxide/commit/2958145a0ae1ef582bbf88352f5567d5c2b5eaf0)) + - [git-odb] refactor ([`1eab15d`](https://github.com/Byron/gitoxide/commit/1eab15dfb42c819050b0277c4cb6a1045d2fd58d)) + - [git-odb] refactor ([`4967c22`](https://github.com/Byron/gitoxide/commit/4967c22340679e953ec6e6319b671455456f93bc)) + - [git-odb] refactor ([`2e68e0c`](https://github.com/Byron/gitoxide/commit/2e68e0c9296977eaaf239b8f0ede6e285b13d06c)) + - [git-odb] fix docs ([`936cfd3`](https://github.com/Byron/gitoxide/commit/936cfd3af731ed822464765f532dd49a206f596d)) + - [git-pack] compilation ([`b392a55`](https://github.com/Byron/gitoxide/commit/b392a55b97a30b10ac0db94a96230e22ea7ab0dc)) + - [git-pack] refactor ([`ea2b3de`](https://github.com/Byron/gitoxide/commit/ea2b3deab78882943e11270e4166ca7c340b03e1)) + - [git-pack] refactor ([`5ca2547`](https://github.com/Byron/gitoxide/commit/5ca25477c44ff6c606901080e25df57371d9ec9c)) + - [git-pack] refactor ([`157b6ff`](https://github.com/Byron/gitoxide/commit/157b6ff7b55ba2b7f8f90f66864212906426f8d7)) + - (cargo-release) version 0.16.0 ([`769c649`](https://github.com/Byron/gitoxide/commit/769c649c00c009bf5a3f7c0611a7b999618f2938)) + - [git-pack] refactor ([`be6ddaa`](https://github.com/Byron/gitoxide/commit/be6ddaa98fc1dcaf77dc0fd9c9d67754e74927e4)) + - [git-pack] refactor ([`1fab6af`](https://github.com/Byron/gitoxide/commit/1fab6af317fd42662c59f82b476917da29cd3c60)) + - [git-pack] refactor ([`e5b00ee`](https://github.com/Byron/gitoxide/commit/e5b00ee257b712477413f48448b0bccf9a06bfaf)) + - [git-pack] the world compiles again ([`f0c0e36`](https://github.com/Byron/gitoxide/commit/f0c0e36a1fb15d44776678567162ac754fdd26c0)) + - [git-pack] used by git-odb ([`5d6ee07`](https://github.com/Byron/gitoxide/commit/5d6ee07a8dec64fe5f68c14c418d922077fad3df)) + - [git-features] refactor to help understand a zlib-related logic bug ([`ae826e8`](https://github.com/Byron/gitoxide/commit/ae826e8c3240efd14939beedd33a06695a6c112b)) + - [git-features] a first step towards supporting a pure rust zlib backend ([`040cab7`](https://github.com/Byron/gitoxide/commit/040cab7f27de83b283957189244d523d71ca1457)) + - [git-odb] refactor ([`e07478c`](https://github.com/Byron/gitoxide/commit/e07478c7b212e4d1d21ce151d9eb26d0fae422a8)) + - [git-odb] fix docs ([`05347d4`](https://github.com/Byron/gitoxide/commit/05347d4154d43d4657839a9cadcebeb1f44ec728)) + - [git-odb] refactor ([`721303d`](https://github.com/Byron/gitoxide/commit/721303db232f87857aae58e12b342e5fb0139306)) + - [git-odb] refactor ([`ea224e9`](https://github.com/Byron/gitoxide/commit/ea224e9ee5f7efcbf4942a2a6fc7e4d790b2be50)) + - [git-odb] refactor ([`6a1b16a`](https://github.com/Byron/gitoxide/commit/6a1b16ae98edc9a694b945a12a7866eb17fc6be3)) + - [git-odb] refactor ([`bae3980`](https://github.com/Byron/gitoxide/commit/bae3980e01131e7da38146aa510d1243e558a01a)) + - [git-odb] refactor ([`6b7400b`](https://github.com/Byron/gitoxide/commit/6b7400bdcfc793d598f2102576939e55a5a3fc43)) + - [git-odb] refactor ([`19ab0cb`](https://github.com/Byron/gitoxide/commit/19ab0cba168cd037107e5cc16a360884d40bd775)) + - [git-odb] refactor ([`47c4042`](https://github.com/Byron/gitoxide/commit/47c4042f16a0e0e6a536bab7150b7cb21958a7ed)) + - [pack-gen] refactor ([`b5618ca`](https://github.com/Byron/gitoxide/commit/b5618cad9f2a2403058b9b73ff1ada53ba85e8d0)) + - (cargo-release) version 0.15.0 ([`d69d9fb`](https://github.com/Byron/gitoxide/commit/d69d9fb0931f8257cef96ef14a89da9340ad9738)) + - Put prodash behind a feature toggle, too ([`966058d`](https://github.com/Byron/gitoxide/commit/966058d611c548e90c050462de52e36f1925e775)) + - Put 'walkdir' behind a feature flag/make it optional. ([`1a3cc5b`](https://github.com/Byron/gitoxide/commit/1a3cc5bea1868ed3ae015403fbe0cdec788be749)) + - Put 'sha1' behind a feature toggle ([`4f326bc`](https://github.com/Byron/gitoxide/commit/4f326bc261c4e7f0d5510df74ad4215da3580696)) + - Put crc functionality behind a feature toggle ([`458fa6e`](https://github.com/Byron/gitoxide/commit/458fa6ec726ec7901c1f6d970cbb1c1ea975dded)) + - Revert "[pack-gen] quick hack for obtaining the entry size more quickly" ([`4c36f92`](https://github.com/Byron/gitoxide/commit/4c36f92880d52886b1fb2c37cf2f98e6b9a327a0)) + - [pack-gen] quick hack for obtaining the entry size more quickly ([`ad6d007`](https://github.com/Byron/gitoxide/commit/ad6d00701d28befda006f41f85bbbb6fc3508e1e)) + - Revert "[pack-gen] remove tree-diff as traversal option." ([`2907a5f`](https://github.com/Byron/gitoxide/commit/2907a5facb08a7decbdfa652e76eb0ebd5e29dcf)) + - [pack-gen] remove tree-diff as traversal option. ([`8373671`](https://github.com/Byron/gitoxide/commit/8373671fd4f3f7e9d78c480e9f68c0a7ae423c69)) + - [pack-gen] fix docs ([`2548b48`](https://github.com/Byron/gitoxide/commit/2548b4813f52409bc1b214485854e5fceb78b534)) + - [pack-gen] basic progress for entry generation ([`953190d`](https://github.com/Byron/gitoxide/commit/953190d70a5df22b54dc1fffe78d41dc7d81cc61)) + - [pack-gen] the first barely working progress ([`5b89a0e`](https://github.com/Byron/gitoxide/commit/5b89a0e4203d405a50bc2e8de9d87b79e545412d)) + - [pack-gen] tests are green ([`34b6a2e`](https://github.com/Byron/gitoxide/commit/34b6a2e94949b24bf0bbaeb169b4baa0fa45c965)) + - [pack-gen] thanks clippy ([`3f948a4`](https://github.com/Byron/gitoxide/commit/3f948a44857b5ff21c85e71ab0c10538862d3d26)) + - [pack-gen] the basics to get the program going ([`03b67b0`](https://github.com/Byron/gitoxide/commit/03b67b09e4127ae4bd791501d74794d9360f7ef6)) + - [pack-gen] Use more light-weight lookups for all blobs ([`80ce34d`](https://github.com/Byron/gitoxide/commit/80ce34d82aebf9a075dde5e77be8af56d22117c7)) + - [pack-gen] refactor ([`e0caf8d`](https://github.com/Byron/gitoxide/commit/e0caf8df5f2d6009a0ef10aa160c7c0bb5682560)) + - [pack-gen] a way to get the pack location by ID right away ([`5619efb`](https://github.com/Byron/gitoxide/commit/5619efb368176809d550dc9d43d820b05a87a700)) + - [pack-gen] refactor ([`fcb9c5f`](https://github.com/Byron/gitoxide/commit/fcb9c5fad04429b7797d400c2a9661a149b5bf66)) + - [pack-gen] refactor ([`11ce2d8`](https://github.com/Byron/gitoxide/commit/11ce2d84c55ef8ffe5ac0a3cf43a48a74ff3185f)) + - [pack-gen] And the fix - all green ([`202c704`](https://github.com/Byron/gitoxide/commit/202c7046283acd65ae3ae6ab5ff0b20b1020e360)) + - [pack-gen] with the addition of write-oack checks it actually fails ([`a9e46a6`](https://github.com/Byron/gitoxide/commit/a9e46a64fc09dabf1290aeafa309bf86cfd496fe)) + - [pack-gen] it compiles and all tests are green, with tests for all parts ([`b3a0344`](https://github.com/Byron/gitoxide/commit/b3a0344db0f10a6208793087a9a9a9bcf39ab47e)) + - [pack-gen] minor but relevant differences between 'existing' and 'existing_object' ([`5f18124`](https://github.com/Byron/gitoxide/commit/5f18124694dd767e378ff6b4e77c71db642b50a2)) + - [pack-gen] very close to a basic impl of count + entries-gen… ([`c927429`](https://github.com/Byron/gitoxide/commit/c9274295e62f59cd8db06a307cc4a69d096a006e)) + - [pack-gen] Fill the relevant information for later ([`932b439`](https://github.com/Byron/gitoxide/commit/932b43998849e5d755f6fd8d19f1e942080e7bbd)) + - [pack-gen] the first test for object counts ([`67b1512`](https://github.com/Byron/gitoxide/commit/67b1512db8c3bdb2ea946d0de96190146be9ed18)) + - [pack-gen] first sketch of how counting could look like ([`6ef0072`](https://github.com/Byron/gitoxide/commit/6ef00723b134d2ce730a288a89858db2ff568c3b)) + - [pack-gen] prep for counting stage ([`93fd425`](https://github.com/Byron/gitoxide/commit/93fd4251885e6a13f0026b96c6688da0e68f9cbf)) + - [pack-gen] tag handling for diff based traversal ([`e55906c`](https://github.com/Byron/gitoxide/commit/e55906c07d9d6f2fbfa5607a2337e586f94beabe)) + - [pack-gen] tag support for tree traversal ([`28ed260`](https://github.com/Byron/gitoxide/commit/28ed260a73554d261c9b00c8ae9a46e66f123e6f)) + - (cargo-release) version 0.10.0 ([`5d7ee6a`](https://github.com/Byron/gitoxide/commit/5d7ee6a105abbb6efeed8624bade936bb59dbc55)) + - [pack-gen] the first green test for Tag iterators ([`df5ef8a`](https://github.com/Byron/gitoxide/commit/df5ef8a53cb4007058137890b414af510025fccf)) + - [pack-gen] A test to see we can handle tag objects ([`1898319`](https://github.com/Byron/gitoxide/commit/189831944d60217a3cd7383a00550d581259f638)) + - Refactor ([`9f0a8cc`](https://github.com/Byron/gitoxide/commit/9f0a8cc1561589088f44a1775832110449a4f1ab)) + - [pack-gen] Finally traversal based pack gen works too ([`086422b`](https://github.com/Byron/gitoxide/commit/086422bbea50bba01060937420ab737e469e11da)) + - [pack-gen] diff-based traversal now finds all reachable objects ([`e819c92`](https://github.com/Byron/gitoxide/commit/e819c92234a1c2b182dd269d0858f003ffcc2cb0)) + - Thanks clippy ([`760febf`](https://github.com/Byron/gitoxide/commit/760febf6a025891957b1afea1dd44a4ed0c4b1ca)) + - [pack-gen] add more objects during diff traversal ([`bc2ef19`](https://github.com/Byron/gitoxide/commit/bc2ef193af15a1414d987b9cc780b2cd3a93e9f4)) + - [pack-gen] pickup more trees ([`2da57bd`](https://github.com/Byron/gitoxide/commit/2da57bd02672d1d4effc940bcf81720fc63f02bc)) + - [pack-gen] Specific tests show that something is off in the changes case… ([`b131c9e`](https://github.com/Byron/gitoxide/commit/b131c9e68c7ac062cd9abbd0541afdb9c69e8649)) + - [pack-gen] central object synchronization for diff based algo as well ([`6de3558`](https://github.com/Byron/gitoxide/commit/6de3558e4becbf4d3cf0640b8eceff40b82f55d3)) + - [pack-gen] have to keep track of all seen objects ([`dc645c6`](https://github.com/Byron/gitoxide/commit/dc645c62a1b05e6b160c8355a71452467ccb6d38)) + - [pack-gen] updating tests to verify something shows that objects are duplicated ([`cef1a58`](https://github.com/Byron/gitoxide/commit/cef1a58cf6cc40fd0a53a9c46ef996f753d7d7d4)) + - [pack-gen] tree diff based pack generation passes the trivial test ([`ad0e345`](https://github.com/Byron/gitoxide/commit/ad0e345af0654ce40afce713de9286f06cf1d05c)) + - [pack-gen] refactor ([`cac002a`](https://github.com/Byron/gitoxide/commit/cac002a94427c10a9f87901a861a9d764126f8e5)) + - [git-traverse] accept tree iterators instead of object ids ([`f343dad`](https://github.com/Byron/gitoxide/commit/f343dad60d34dfd88247a14c7e3de906a761cf2d)) + - [pack-gen] Most of changes based pack gen ([`9ade04c`](https://github.com/Byron/gitoxide/commit/9ade04c47b3d4cad29a754f15f21df7e1b266325)) + - [pack-gen] prepare diff based pack-gen ([`fa2ae2c`](https://github.com/Byron/gitoxide/commit/fa2ae2c924b295a4c25f41ba9ecbcf5c45b77e85)) + - [git-diff] refactor ([`087e853`](https://github.com/Byron/gitoxide/commit/087e85367c27bb3684c6ad543c7eae46762e5e44)) + - [git-traverse] refactor ([`85de287`](https://github.com/Byron/gitoxide/commit/85de2874087f64fc166797a3219eeb26be460619)) + - (cargo-release) version 0.3.0 ([`684de4b`](https://github.com/Byron/gitoxide/commit/684de4b376ecd4cc5330f7ac8643352ea9580ed3)) + - [pack-gen] Speed up tree-traversal :D ([`90b4093`](https://github.com/Byron/gitoxide/commit/90b4093aa6076c97f751013de4c25934fef764b8)) + - Thanks clippy ([`009a342`](https://github.com/Byron/gitoxide/commit/009a3425d24d4c9f476ff1c32da9b279cb170350)) + - [pack-gen] Probably a valid impl of tree traversal ([`4c72a17`](https://github.com/Byron/gitoxide/commit/4c72a171d50d08d4b35209fcef107b9a85a6c648)) + - [pack-gen] quite a bit closer to tree-traversal for pack gen ([`ecd37ee`](https://github.com/Byron/gitoxide/commit/ecd37eea0154791bf9192d1225828e7d9b5ad530)) + - [pack-gen] refactor ([`325d63e`](https://github.com/Byron/gitoxide/commit/325d63efe6855c8d14d564d8b3cbce9a9e144d14)) + - [pack-gen] a test for upcoming traversal modes ([`8d1e9ac`](https://github.com/Byron/gitoxide/commit/8d1e9ace79bbbe41ea4fac70c13522d7d6091a81)) + - [pack-gen] refactor ([`08b136f`](https://github.com/Byron/gitoxide/commit/08b136f0cf35f8b275feee9830bfab4555d40a99)) + - (cargo-release) version 0.15.0 ([`d91b241`](https://github.com/Byron/gitoxide/commit/d91b2412381e3c8c1f24c38469e821c3c3960e34)) + - (cargo-release) version 0.9.0 ([`84897fd`](https://github.com/Byron/gitoxide/commit/84897fd8e6e1b0269da0303d6a0de8f9e0eb58e5)) + - Merge branch 'patch-2' ([`f01dc54`](https://github.com/Byron/gitoxide/commit/f01dc54010683b232c5f5813bd5370e93f1681f5)) + - Refactor ([`a9e4feb`](https://github.com/Byron/gitoxide/commit/a9e4feb0a81894568be730603446e2d061dd558f)) + - Fix formatting ([`a341995`](https://github.com/Byron/gitoxide/commit/a341995e6014b6ed0e43ae94fa1152aed6fcfd89)) + - Merge branch 'patch-1' ([`5edc076`](https://github.com/Byron/gitoxide/commit/5edc0762524112bb6716b3afcf23b2a4a0f5efd3)) + - Use Seek to skip large objects during indexing. ([`95e2af7`](https://github.com/Byron/gitoxide/commit/95e2af74574af998294265b6a3de833dbe4dcedb)) + - Remove almost all unsafe code from Tree. ([`42b6033`](https://github.com/Byron/gitoxide/commit/42b6033f3c367ccce37c82356183d165d37ae881)) + - Thanks clippy ([`17258cc`](https://github.com/Byron/gitoxide/commit/17258cc58767caa6e71227898decd160ad0cdf13)) + - Thanks clippy ([`09decde`](https://github.com/Byron/gitoxide/commit/09decde782e0b9e794a740d4fa654af73398d80a)) + - Convenience methods for iterators ([`aa6c9e6`](https://github.com/Byron/gitoxide/commit/aa6c9e699a09b6b2b4f55aa75a1dd6f648eead09)) + - Refactor ([`d9783b9`](https://github.com/Byron/gitoxide/commit/d9783b94b0149c584690a1a50f029c9424de08c3)) + - A sketch of convenient finding of commits ([`db21062`](https://github.com/Byron/gitoxide/commit/db210622b95d5f1f24d815cb35db5d46aa8a09e3)) + - Refactor ([`3af7b7b`](https://github.com/Byron/gitoxide/commit/3af7b7b2bc0082298faa7ff2bd4413e80bee1107)) + - Sketch of convenience function for `Find` trait. ([`2bf4958`](https://github.com/Byron/gitoxide/commit/2bf4958dd7d1ad0a2f9f8c5754be88c3efb524a4)) + - Refactor ([`bd4d21e`](https://github.com/Byron/gitoxide/commit/bd4d21e7003801319a62887e3d39467b2ee1ad0d)) + - Refactor ([`8b10434`](https://github.com/Byron/gitoxide/commit/8b1043483cb46fd1b7f47a90c9dce24a65d58d1b)) + - Fix order of operations in git-odb::hash::Write ([`a31d8c7`](https://github.com/Byron/gitoxide/commit/a31d8c75e7821b68b49f017010646a8232ecc6cc)) + - (cargo-release) version 0.14.0 ([`a760f8c`](https://github.com/Byron/gitoxide/commit/a760f8c013e13ba82daa1acf1a4a57e0818a008d)) + - (cargo-release) version 0.14.0 ([`d9514ee`](https://github.com/Byron/gitoxide/commit/d9514eec64579ef77c9f2ac5dfe87cd302180eb9)) + - Rename 'Locate' to 'Find' - shorter and just as good ([`60f72f5`](https://github.com/Byron/gitoxide/commit/60f72f573a7696323e09bf4add80d5fbce22c99d)) + - (cargo-release) version 0.13.0 ([`5c791af`](https://github.com/Byron/gitoxide/commit/5c791af217fac6a171d174ad9f4ee5f4d5282892)) + - [traversal] remove git-odb::traversal (now git-traverse::iter) ([`35b74d2`](https://github.com/Byron/gitoxide/commit/35b74d2f046426d99bb5431f8aea42ac453ac101)) + - Prepare test utilities for release… ([`d35e654`](https://github.com/Byron/gitoxide/commit/d35e654747f96cec93bdecd1314ce325129cbc44)) + - (cargo-release) version 0.8.0 ([`a1ce210`](https://github.com/Byron/gitoxide/commit/a1ce210003ff07bf11291018bb182cbc7913647b)) + - (cargo-release) version 0.3.0 ([`e9665c7`](https://github.com/Byron/gitoxide/commit/e9665c784ae7e5cdaf662151395ee2355e9b57b6)) + - [traversal] all the caching ([`0890403`](https://github.com/Byron/gitoxide/commit/0890403cce658ea90c593a6ca21e24f02ddf5a93)) + - [tree-diff] first prototype of traversal experiment ([`ece43b4`](https://github.com/Byron/gitoxide/commit/ece43b4b0bf054d798685461d2b96daaafd8a408)) + - Thanks clippy ([`2d5e205`](https://github.com/Byron/gitoxide/commit/2d5e20520499d1a87808db508548b408e3777d0e)) + - [tree-diff] more tests for the tree iterator ([`91b5a02`](https://github.com/Byron/gitoxide/commit/91b5a029337200a2873a21696020dcda08e335cb)) + - [tree-diff] Now the commit graph traversal works with zero-allocations ([`8078910`](https://github.com/Byron/gitoxide/commit/8078910b27149df10b6b236b9311ebee31523710)) + - Make it easy to get a commit iterator ([`33213f3`](https://github.com/Byron/gitoxide/commit/33213f30abbb6619d41663a4baf3078af3284085)) + - [tree-diff] refactor into iterator based model ([`29b527a`](https://github.com/Byron/gitoxide/commit/29b527aaea101c9b4e885db1c6d3533ef2310c54)) + - [tree-diff] The least intrusive way to allow dealing with tree iterators ([`d41dd3c`](https://github.com/Byron/gitoxide/commit/d41dd3c38ee34b250a4f5de120d7ae3e04e3212d)) + - Refactor ([`a4d5f99`](https://github.com/Byron/gitoxide/commit/a4d5f99c8dc99bf814790928a3bf9649cd99486b)) + - Refactor ([`03ee510`](https://github.com/Byron/gitoxide/commit/03ee510a5f9c24b6acddaec1d30ea3ad39174603)) + - Better ergonomics for accessing decoded objects ([`ae3eab6`](https://github.com/Byron/gitoxide/commit/ae3eab6d6e4b96e207372fa8cb82f5ac9833e5e4)) + - Refactor ([`c1013dd`](https://github.com/Byron/gitoxide/commit/c1013dddbc221b366b91d186cfd1732f1d72be10)) + - Refactor ([`f37c31f`](https://github.com/Byron/gitoxide/commit/f37c31f04bf8cf531284abe380db77d6196bd711)) + - Refactor ([`ac70651`](https://github.com/Byron/gitoxide/commit/ac706518fff2e92ade3589dea4a6c81fca57aec2)) + - Refactor ([`77764f3`](https://github.com/Byron/gitoxide/commit/77764f3b9c3e8202119bb9327e150089c3ecbb9b)) + - Refactor ([`3185cc9`](https://github.com/Byron/gitoxide/commit/3185cc9de1f7d3e52d088b60fcaae0ac91a72fe1)) + - Thanks, cargo audit ([`4f293f5`](https://github.com/Byron/gitoxide/commit/4f293f5036c44a69ccacf102d35202adad83bbe0)) + - Refactor ([`edf7d38`](https://github.com/Byron/gitoxide/commit/edf7d382148aa139485c8279c2a50dc6c86d481d)) + - Refactor ([`ca98221`](https://github.com/Byron/gitoxide/commit/ca98221d5a512dabf683cc1da56d40a17285f2fb)) + - Refactor ([`b4027e3`](https://github.com/Byron/gitoxide/commit/b4027e3df187931a263798b255c80b272910aef7)) + - Refacto ([`6e328da`](https://github.com/Byron/gitoxide/commit/6e328da9f8a73ac8e699aea55b1250a433f5ecd9)) + - Fix docs ([`a54bab4`](https://github.com/Byron/gitoxide/commit/a54bab40a5881873eb2b1c591fa9f05d8034ac6d)) + - Refactor ([`3f2ee4c`](https://github.com/Byron/gitoxide/commit/3f2ee4cda6db14902639f7fc3a9fbee05508086f)) + - Refactor ([`d6ab581`](https://github.com/Byron/gitoxide/commit/d6ab581db66c1d09578ed2af9db34929995e2cb9)) + - Refactor ([`d490b65`](https://github.com/Byron/gitoxide/commit/d490b65ebbc6666cd59d88f8677dc1c52bfe1e1c)) + - Pack V2 writing (base objects only) seems to work now #(67) ([`e68dd84`](https://github.com/Byron/gitoxide/commit/e68dd84df7d13efcc7882644d3d9347b3722785a)) + - The first more thorough and indirect validation of the newly written pack… ([`d43687e`](https://github.com/Byron/gitoxide/commit/d43687ed9093224e0caba4063063705b9473afd0)) + - Refactor ([`08fafaa`](https://github.com/Byron/gitoxide/commit/08fafaa03144fc3ddea9120a4a1943e18c454ae8)) + - Test newly written pack data alone ([`01fdd70`](https://github.com/Byron/gitoxide/commit/01fdd70395a662612309ece730c2a75303e2155e)) + - Write pack data entries #(67) ([`722202e`](https://github.com/Byron/gitoxide/commit/722202edce0d5700a9df9eff6208ad5d7c6554fb)) + - Refactor ([`eed1e3c`](https://github.com/Byron/gitoxide/commit/eed1e3c21658ee152d224622599cd5a4c65df126)) + - Write pack data header #(67) ([`717726b`](https://github.com/Byron/gitoxide/commit/717726b30e80f0ca56927f31e823ec48470fbeb2)) + - Refactor ([`28cbeb3`](https://github.com/Byron/gitoxide/commit/28cbeb3ca1d6c5f6aa7664255d1d44fdb49f116b)) + - Refactor ([`4261c7d`](https://github.com/Byron/gitoxide/commit/4261c7dea7666cfc3a867bca2bbdb0827487be00)) + - All logic needed to write a valid pack within an iterator ([`775ab29`](https://github.com/Byron/gitoxide/commit/775ab295531875ec93e57d30422b88e03e48313e)) + - Sketch of pack data write API ([`dfeda87`](https://github.com/Byron/gitoxide/commit/dfeda87de13c6f05a39732d3f0518bb76695be9a)) + - Refactor ([`f33fa10`](https://github.com/Byron/gitoxide/commit/f33fa10224d46539c94a2042014c14042d7dc968)) + - [experiment/object-access] allow bare repositories ([`401690d`](https://github.com/Byron/gitoxide/commit/401690dbc6c10b2e7144bf3362c4b2e71435e801)) + - Thanks clippy ([`c86823a`](https://github.com/Byron/gitoxide/commit/c86823a5cce91a12738c25313ae15eec7751af46)) + - Refactor zlib ([`4587b82`](https://github.com/Byron/gitoxide/commit/4587b8244c5ba85aa899e998214119aadb948862)) + - Refactor zlib ([`496e6bb`](https://github.com/Byron/gitoxide/commit/496e6bb86ba1bcf66ffaf250b026c12bd3e830c5)) + - Refactor ([`3a4469c`](https://github.com/Byron/gitoxide/commit/3a4469c20c44dd649a442c7f6c2902325c744394)) + - First basic pack entry generation (base only) works… ([`75cb32b`](https://github.com/Byron/gitoxide/commit/75cb32baed75c23b47d6422569be630c6fd412f7)) + - Refactor ([`d4bf8ae`](https://github.com/Byron/gitoxide/commit/d4bf8aea9b8f811b57b943be16ea4bb2eabccca4)) + - Refactor ([`2d89222`](https://github.com/Byron/gitoxide/commit/2d89222b1d48cf63544ceb4bf8d3067a49adb792)) + - Refactor ([`eb3a8da`](https://github.com/Byron/gitoxide/commit/eb3a8da7a246355f1ef0de20226abaaf38b01126)) + - Allow calling 'finalize()' on the entries iterator ([`3c617bc`](https://github.com/Byron/gitoxide/commit/3c617bc2ae59adbb12c254308269e745149d462b)) + - Refactor ([`b7d0323`](https://github.com/Byron/gitoxide/commit/b7d03235b1eb42e98cfc7620dea9d41b0e87d208)) + - Reduce size of data::Object ([`7aa783a`](https://github.com/Byron/gitoxide/commit/7aa783ab4e06d3e33f918c0cd084dd8d89f3d768)) + - First pack-to-pack copy and crc32 verification ([`37619f0`](https://github.com/Byron/gitoxide/commit/37619f0ea71216ef7a0b9e512e3987fead08c9b9)) + - It's possible to get entry data within pack generation ([`a2a5927`](https://github.com/Byron/gitoxide/commit/a2a59272116029e9328fb2798e5c72e9fc9b3b32)) + - Git-odb without cargo warnings due to using the same test twice ([`8945f95`](https://github.com/Byron/gitoxide/commit/8945f95364b489e7a639d74dd0f28b17e82e70f3)) + - A way to obtain entry information using previous lookup information ([`a55d113`](https://github.com/Byron/gitoxide/commit/a55d113ded8d6aeee78f9041f13167dc54243254)) + - Refactor ([`95ab11b`](https://github.com/Byron/gitoxide/commit/95ab11bd3014c81ab35437ba9d1c6b84caf6ba76)) + - A probably more secure way of accessing pack data ([`7a01bd8`](https://github.com/Byron/gitoxide/commit/7a01bd8b120fa34566b0f97ca9b35e1d8a97aefa)) + - Sketch of pack-entry retrieval api ([`d1e9248`](https://github.com/Byron/gitoxide/commit/d1e92486c9b716c11cf75eccd9829738d3b94ca0)) + - Refactor ([`08cf90a`](https://github.com/Byron/gitoxide/commit/08cf90a2e33e3a0cdbb249ffcc46ef3a46685145)) + - Let's be a bit more conservative with this information for now ([`efef417`](https://github.com/Byron/gitoxide/commit/efef417e52caf12a2090b6d4a96e0633e77471dd)) + - Objects know their pack location ([`73f1c66`](https://github.com/Byron/gitoxide/commit/73f1c668b629055d8b0bffc1a6cc31c54037a6da)) + - Chunks based iteration for pack generation ([`23c2694`](https://github.com/Byron/gitoxide/commit/23c2694c86eb397f5063f248c95cd164bae2120a)) + - Some notes about how to treat defaults of file versions ([`cfa5cf6`](https://github.com/Byron/gitoxide/commit/cfa5cf6146d4de028a31b5eb8ad756477e37111b)) + - Run git-odb tests in parallel, too; improved threaded error handling ([`40802fd`](https://github.com/Byron/gitoxide/commit/40802fd8bbb15b8a61249522d67f3a5b28da64b3)) + - The first test for pack generation ([`2a2fdde`](https://github.com/Byron/gitoxide/commit/2a2fdde2e5365e83faf99999ea1c640159f5c4b9)) + - Refactor ([`385f52d`](https://github.com/Byron/gitoxide/commit/385f52d4ea99230839bb447e2993bad741ce0cae)) + - Refactor Please enter the commit message for your changes. Lines starting ([`f65c68c`](https://github.com/Byron/gitoxide/commit/f65c68c3c7c4c838ea77494ecc0ce17f6d5d719b)) + - Fix doc links ([`ec35743`](https://github.com/Byron/gitoxide/commit/ec35743cc4062f2b6dbfc85b7f5aadfa68f598a7)) + - Thanks clippy ([`563e445`](https://github.com/Byron/gitoxide/commit/563e4452aae5c6dc1323e0f6759315e73f3a2c89)) + - The first seemingly working iteration over all objects in an odb #(67) ([`6b34f62`](https://github.com/Byron/gitoxide/commit/6b34f62cc4e6f9ee6030590d8b3f185dda3bc568)) + - Refactor ([`01d9d91`](https://github.com/Byron/gitoxide/commit/01d9d91d1bce6217b8a48ab1f0a7ba4e17508279)) + - Impl size_hint for linked db iter ([`ada259b`](https://github.com/Byron/gitoxide/commit/ada259b4fe441728682521e9138ed9f6ef1c13f4)) + - Refactor ([`82c2f42`](https://github.com/Byron/gitoxide/commit/82c2f428e22c3cda79913c9ca2f092c377d692aa)) + - Refactor ([`7a6b514`](https://github.com/Byron/gitoxide/commit/7a6b514a5b9b93bf574cd3a114f27ad5967e89ac)) + - First sketch of object iterator for linked::Db ([`a316eed`](https://github.com/Byron/gitoxide/commit/a316eed4bf4634d4776d153528cb28254b847bdd)) + - Set environment in testtools to freeze repositories generation scripts ([`eaad3ab`](https://github.com/Byron/gitoxide/commit/eaad3ab69338115439a553ba1062160dc3a08082)) + - Faster repeated tests if fixtures don't change ([`792277f`](https://github.com/Byron/gitoxide/commit/792277f241446086dd6c9b78f688363d4e66e5a7)) + - Refactor ([`e1a92ad`](https://github.com/Byron/gitoxide/commit/e1a92adbedcb017a9e35049389ef86fca34fa44c)) + - Allow the use of shared test utilities across crates ([`b117626`](https://github.com/Byron/gitoxide/commit/b117626df6da714c24d2b7914301678e89d2d0cb)) + - Refactor ([`40b86a7`](https://github.com/Byron/gitoxide/commit/40b86a78367fbd7cd9c8e5447c9b97fa685cc43e)) + - Refactor ([`8b00094`](https://github.com/Byron/gitoxide/commit/8b0009466b820b934a2244a98360007336180246)) + - Fix doc links ([`7479071`](https://github.com/Byron/gitoxide/commit/747907145e001a093c8dc84e80d879f4d18c84d5)) + - Thanks clippy ([`6f901f5`](https://github.com/Byron/gitoxide/commit/6f901f5daa868c1a0e9cea113abe13beb65d8f35)) + - Ancestor iterator is now generic over Locate trait ([`bbfd616`](https://github.com/Byron/gitoxide/commit/bbfd616ae023aae9d92ebd9d873a9be02423e820)) + - [fail] try to abstract ancestor::Iter over Locate trait ([`f8c0375`](https://github.com/Byron/gitoxide/commit/f8c0375bbafffc81938998a8ff8aa2faac9253e1)) + - Refactor ([`5ef1f22`](https://github.com/Byron/gitoxide/commit/5ef1f22c1e12ff8d607663d4dfbbbfe426a29e0f)) + - Improve interface for building packs based on Locate trait #(67) ([`5b66b6e`](https://github.com/Byron/gitoxide/commit/5b66b6e729c858068a31e4817db63a5f6ba5032b)) + - A version of the Locate trait we can do today #(67) ([`d752be2`](https://github.com/Byron/gitoxide/commit/d752be2931e3403c16fea8d804c8759c56bb1fd4)) + - [git-odb] Associated types with lifetimes also don't seem to work ([`0e68a9d`](https://github.com/Byron/gitoxide/commit/0e68a9d095eb038da0e9139fe1d649d593d72010)) + - [git-odb] Trying to use offical traits won't work with our kind of object ([`29a5054`](https://github.com/Byron/gitoxide/commit/29a5054740fd0c7958376c603fd6214421f7772f)) + - :borrowed::Object => git-odb::data::Object ([`747a13e`](https://github.com/Byron/gitoxide/commit/747a13e9a1fe5200c53055dd961507c9fef667e1)) + - An even better name for decode errors ([`f270850`](https://github.com/Byron/gitoxide/commit/f270850ff92eab15258023b8e59346ec200303bd)) + - Make clear it's a decode error we are using there ([`f45cb4b`](https://github.com/Byron/gitoxide/commit/f45cb4b62122559e5701923e0a23dd5791ee2ced)) + - Rename git-object::(owned->mutable)|(borrowed|immutable) #(67) ([`91ee558`](https://github.com/Byron/gitoxide/commit/91ee55893bf4b27a47d86d51bae6f99b59b69147)) + - Bump git-odb minor version ([`5c833ce`](https://github.com/Byron/gitoxide/commit/5c833ce64babd00b7ced3e3a1c9ed3dbd260c9f4)) + - Thanks clippy ([`547af6e`](https://github.com/Byron/gitoxide/commit/547af6e3965112c8eea69cae173a6996249b77c5)) + - Fix test breakage for loose object reading ([`222c7a2`](https://github.com/Byron/gitoxide/commit/222c7a276efdc65da4f9490f53b82e58f8e878c1)) + - Fix docs #(67) ([`01db10a`](https://github.com/Byron/gitoxide/commit/01db10a27431ad89a68ed3e4eabae810748a6f29)) + - Thanks clippy ([`60a7689`](https://github.com/Byron/gitoxide/commit/60a7689d7493d29103775ce358999314af9257c8)) + - Refactor ([`ef674ff`](https://github.com/Byron/gitoxide/commit/ef674ffde5af3c19a9538d99112f414144b921cd)) + - Remove loose::Object entirely #(67) ([`5cf4840`](https://github.com/Byron/gitoxide/commit/5cf4840b10a3fac43266bc9defa72977a004bf8c)) + - Start using loose::Db::locate2 - definitely still bugs in there ([`d6f07b7`](https://github.com/Byron/gitoxide/commit/d6f07b7709fb2291484859477b54371ef3108a57)) + - An alternative version of loose::Db::locate() for use with borrowed::Object ([`5b40a32`](https://github.com/Byron/gitoxide/commit/5b40a32c017f264d80b8babb293470a4a47a45b4)) + - Refactor ([`bad3ce4`](https://github.com/Byron/gitoxide/commit/bad3ce417dd7b4cdbcf45c95fbdc44b245b0762f)) + - Replace loose::Object::stream() with *::data() #(67) ([`040b347`](https://github.com/Byron/gitoxide/commit/040b347d1b020ef17a8862c4cb792e267d674c8a)) + - Sketch loose::Object::data() to start refactoring #(67) ([`ee1701f`](https://github.com/Byron/gitoxide/commit/ee1701f681af4a6acfd6809fe439a3fa1912f259)) + - Sketch of trait for locating objects #(67) ([`31445d7`](https://github.com/Byron/gitoxide/commit/31445d778864c430d363bea86c51286f5f9f69a1)) + - Refactor ([`2754dd6`](https://github.com/Byron/gitoxide/commit/2754dd6078608a600ec20a5d1c9307c2d746e6c5)) + - Refactor ([`3e908bd`](https://github.com/Byron/gitoxide/commit/3e908bd4b4077c4a5d113cefc113f9d71f249133)) + - Refactor ([`409d763`](https://github.com/Byron/gitoxide/commit/409d763d2fca974a647487c72d15f568a9b62ccb)) + - Refactor ([`896ab94`](https://github.com/Byron/gitoxide/commit/896ab940bcd475d026e4009b3aa2fa6a025c14bc)) + - Remove unsafe interface for stepped computation #(67) ([`c856613`](https://github.com/Byron/gitoxide/commit/c856613a35aea7dea1d093bfcfe1ddbde93fdf26)) + - Show that with custom iterators, Arc's are natively supported #(67) ([`0c49007`](https://github.com/Byron/gitoxide/commit/0c490073c53cf1f2df9fe2cd7612a1531e1430a7)) + - Thanks clippy ([`405dd9d`](https://github.com/Byron/gitoxide/commit/405dd9d4cb7980a4925b19562e02a9fb7f0f5ab6)) + - Multi-tip support #(67) ([`2254ecc`](https://github.com/Byron/gitoxide/commit/2254ecc4b1927867f02fe03db4a027d8c1e47ee9)) + - Cache support for traversal #(67) ([`1e9300a`](https://github.com/Byron/gitoxide/commit/1e9300ac53b1d3e96352ce466f2c7d27a93ade2a)) + - Cycle and duplicate check #(67) ([`334a72d`](https://github.com/Byron/gitoxide/commit/334a72d4a2ec2718d92b9c0843c4f6722a909f5e)) + - A new failing test ([`86b6c24`](https://github.com/Byron/gitoxide/commit/86b6c2497cfa17bf3f822792e3afe406f7968ee7)) + - Refactor ([`5408b62`](https://github.com/Byron/gitoxide/commit/5408b6258c5c5aea26c91e4ec7e7d56e8a3cc8f8)) + - The first basic traversal utility #(67) ([`ea6610b`](https://github.com/Byron/gitoxide/commit/ea6610b9157d8d0dabd2ddd07c45dc6651b9cf84)) + - Fix iteration signature due to shadowed naming ([`fe8b459`](https://github.com/Byron/gitoxide/commit/fe8b459fc406d5fee39d7dd333ff0afad78a0c38)) + - Thanks clippy ([`a463a43`](https://github.com/Byron/gitoxide/commit/a463a438c69a96ac0f291298113c7814b6d51ec4)) + - Sketch of machinery for producing pack entries #(67) ([`ac8e7fb`](https://github.com/Byron/gitoxide/commit/ac8e7fb6c8ae4ac42f56482d9d7744aa66132702)) + - A step towards using SteppedReduce #(67) ([`0d5595a`](https://github.com/Byron/gitoxide/commit/0d5595a2269314d9aa2a76b2b5d650506a51f58e)) + - (cargo-release) version 0.13.0 ([`ac2eddb`](https://github.com/Byron/gitoxide/commit/ac2eddb06eb3d8a9a3dcdcd796eb54a7e45ab935)) + - Allow parallel reducers to produce something during 'feed()' #(67) ([`6c04fcd`](https://github.com/Byron/gitoxide/commit/6c04fcd643083d9db633edd3bb838b4f5de8f0db)) + - Allow more fine-grained stepping over the pack generator #(67) ([`22eb892`](https://github.com/Byron/gitoxide/commit/22eb892e7e66f6a5e3e35094a657a8469a163e26)) + - Allow to obtain object information without fetching the data #(67) ([`6553850`](https://github.com/Byron/gitoxide/commit/6553850aacbf815989af297bc95fe15d915f62ec)) + - Sketch a version to abstract object data retrieval #(67) ([`ad90446`](https://github.com/Byron/gitoxide/commit/ad90446da913f1f0b9833a393c5f33ae2638ae30)) + - Implement `Write` trait for linked::Db ([`21362c3`](https://github.com/Byron/gitoxide/commit/21362c388026837ad78891945cfeac8cea27e0db)) + - Docs for `linked::Db` ([`9d936de`](https://github.com/Byron/gitoxide/commit/9d936dea06b8b28a46e8a0a466ea9f4d618595b1)) + - Support for caches within linked::Db ([`3635a3e`](https://github.com/Byron/gitoxide/commit/3635a3e8629143c6b96ed80eb7d7a10f011afceb)) + - `locate()` for `linked::Db` without cache for now ([`014bc3c`](https://github.com/Byron/gitoxide/commit/014bc3c74a8b566608091f0decfe79439ab5d6f9)) + - Refactor ([`7b443d1`](https://github.com/Byron/gitoxide/commit/7b443d1563e8231ca8bf88752eac28f441801d52)) + - Refactor ([`d077ead`](https://github.com/Byron/gitoxide/commit/d077ead603ce87f891e83e83cbcffeb4c79dd1f0)) + - :Db::init() with a few tests ([`4c77e4c`](https://github.com/Byron/gitoxide/commit/4c77e4c97641ab3b02b56aaa702a7d2ca5bced7c)) + - Frame for linked::Db ([`e64d148`](https://github.com/Byron/gitoxide/commit/e64d148a0984fb6dba3f788f8cc99c37914fd930)) + - Make cycles in alternate object chains fatal ([`67e679a`](https://github.com/Byron/gitoxide/commit/67e679a6d7b56c2754f422e5cce3f8cf0784e506)) + - Resolve alternates as paths, not as repositories ([`73352c3`](https://github.com/Byron/gitoxide/commit/73352c346d4a408eb657f1862996525982c16db6)) + - Remove support for Polonius in preparation for a new repo type ([`871c803`](https://github.com/Byron/gitoxide/commit/871c803d9c5be6e786338b549c243ad50d057df5)) + - (cargo-release) version 0.11.0 ([`fd698e3`](https://github.com/Byron/gitoxide/commit/fd698e334e44d5c478c162f98d09afd9ce7a6895)) + - Introduce pack_id for use in pack cache, preventing (most collisions) ([`ad04ad3`](https://github.com/Byron/gitoxide/commit/ad04ad3b8ac54e78bee307dd44c85c1389edced2)) + - Fix benchmark to get valid test results ([`20abb3a`](https://github.com/Byron/gitoxide/commit/20abb3a4fc9769f23b9a86d2e8d49f53290b36f4)) + - First use of memory-cap based LRU cache for object access ([`b057494`](https://github.com/Byron/gitoxide/commit/b0574945039881c6d5d8be4107c1c987ed3bbaf6)) + - Add hash-map based LRU to allow bigger/more effective object caches ([`5affdd5`](https://github.com/Byron/gitoxide/commit/5affdd5df0c4d01f3130fc1be259c72f601a1f71)) + - Feature toggle for uluru based Lru cache ([`98eec48`](https://github.com/Byron/gitoxide/commit/98eec4837d605a408b026a859e53a7e2eae8e4da)) + - Refactor ([`d624d09`](https://github.com/Byron/gitoxide/commit/d624d097784eed216f8d0e94544d8b62ef6c3010)) + - Improve docs to prevent people to 'misuse' the Lru cache. ([`fff62ed`](https://github.com/Byron/gitoxide/commit/fff62ed708153e1c9313930bf36877faad5cd777)) + - LruCache with const-generics ([`93618d1`](https://github.com/Byron/gitoxide/commit/93618d107e9defadb603209251f77948caddc121)) + - [experiment] cached version of compound::locate() ([`ec988dc`](https://github.com/Byron/gitoxide/commit/ec988dc21320b211f3da9327b63101f954db307e)) + - (cargo-release) version 0.10.0 ([`3161777`](https://github.com/Byron/gitoxide/commit/316177729e42f8d000a40ab01b9b97621e7179e8)) + - (cargo-release) version 0.7.0 ([`b900914`](https://github.com/Byron/gitoxide/commit/b900914a00292217ba7b9bcac260591800395287)) + - (cargo-release) version 0.12.0 ([`3b71e7e`](https://github.com/Byron/gitoxide/commit/3b71e7e8416e550b47e5aed2259c1181497ac9e8)) + - (cargo-release) version 0.2.0 ([`4ec09f4`](https://github.com/Byron/gitoxide/commit/4ec09f4d2239ea1d44f7145027e64191bf2c158c)) + - Greatly reduce compound::Object size ([`afa8156`](https://github.com/Byron/gitoxide/commit/afa8156c37c6ea93bad7553e5a373fc333398d9b)) + - The git-odb compound Object clearly is too large ([`8f0e813`](https://github.com/Byron/gitoxide/commit/8f0e8138ed3313b79b4e358854b9fda5e981f652)) + - Add link to simplified/polonius version in the docs ([`d53c4b0`](https://github.com/Byron/gitoxide/commit/d53c4b0f91f1b29769c9430f2d1c0bcab1170c75)) + - Only check alternates for objects not found in packs or loose ([`b317200`](https://github.com/Byron/gitoxide/commit/b317200b72096573d511d229c6e61e74e7ba14db)) + - Avoid double-lookup in packs without polonius ([`eaae9c1`](https://github.com/Byron/gitoxide/commit/eaae9c1bc723209d793eb93f5587fa2604d5cd92)) + - Thanks clippy ([`0c5f404`](https://github.com/Byron/gitoxide/commit/0c5f4043da4615820cb180804a81c2d4fe75fe5e)) + - Remove locate(…) -> Option<Result<…>> in favor of Result<Option<…>> ([`40ee743`](https://github.com/Byron/gitoxide/commit/40ee7438a98c4094c0fd04977cd4904668087512)) + - A locate returning Result<Option<Object>> for compound DB ([`a1dafa6`](https://github.com/Byron/gitoxide/commit/a1dafa64b4e26dd1456d38f94d58eaadf19abfd3)) + - Use Result<Option<Object>> in Bundle::locate() ([`2dfef8f`](https://github.com/Byron/gitoxide/commit/2dfef8f71da456c5c494e1530040589582a046b1)) + - A trial for Result<Option<Object>> for loose object databases ([`3842859`](https://github.com/Byron/gitoxide/commit/3842859c5bddb8b4583443685c26dcae3f8db558)) + - Assure loose objects are actually not found when opening ([`7a4f2bf`](https://github.com/Byron/gitoxide/commit/7a4f2bf2cb31407422be2e563b3df210bbf8bfd0)) + - Add feature toggle for polonius and make it part of the test suite ([`c825c11`](https://github.com/Byron/gitoxide/commit/c825c11e2d17141b38654d30b37e043dfae383f3)) + - (cargo-release) version 0.9.1 ([`e0feb28`](https://github.com/Byron/gitoxide/commit/e0feb28b50ce55be71b24ea5238a760f5b1f8d3b)) + - (cargo-release) version 0.9.0 ([`efc8983`](https://github.com/Byron/gitoxide/commit/efc898381d830e44487c62e35a665d3ccd0a2d39)) + - Thanks clippy ([`0fc239c`](https://github.com/Byron/gitoxide/commit/0fc239cf9b773f72928b7c42344b578c6ff5d19f)) + - Refactor ([`f2e9add`](https://github.com/Byron/gitoxide/commit/f2e9add3cb5803426a2e36a3b462f823e8cef44b)) + - Upgrade depdendencies ([`e4a7711`](https://github.com/Byron/gitoxide/commit/e4a77112ee4f5d0ab61d0678aab8ee090335740c)) + - Fix compile warnings produced by +nightly build ([`e387d2c`](https://github.com/Byron/gitoxide/commit/e387d2c148e231321f88e5fb1b2988437475d2c0)) + - Merge pull request #50 from Byron/edward-shen/odb-zlib-ng ([`acb90d7`](https://github.com/Byron/gitoxide/commit/acb90d755fb02c37f8a5a431778abcbe143fb5e5)) + - Conform imports ([`fd73731`](https://github.com/Byron/gitoxide/commit/fd737317379af80f8e0ba9a9a8fc505fb60fd177)) + - Fix error type argument order and spell fields out ([`819568e`](https://github.com/Byron/gitoxide/commit/819568e9c5be14cec1e9e1cdc915b4c286c2ed00)) + - [git-odb] Return error on invalid packs ([`88de64d`](https://github.com/Byron/gitoxide/commit/88de64d433b44996d5f8be733b50e1949c71e42d)) + - [git-odb] Fix Inflate::once ([`36f6bbd`](https://github.com/Byron/gitoxide/commit/36f6bbd451a5474cb6dac0259904e4aed7fd11d9)) + - [git-odb] Remove unnecessary tests ([`ebe41ca`](https://github.com/Byron/gitoxide/commit/ebe41cadc4acb38326e59d193fd3b1e501146943)) + - [gix] Use flate2 by default ([`f1158a1`](https://github.com/Byron/gitoxide/commit/f1158a1a4bc8e13913461db4d4851e32d57816ff)) + - [gix] Add optional zlib feature ([`f1f9665`](https://github.com/Byron/gitoxide/commit/f1f96658a6cd6165ba9c9d7acb809fcaf2c46f9c)) + - [git-odb] Add feature flag for zlib-ng ([`96b3810`](https://github.com/Byron/gitoxide/commit/96b3810995f9e7b0164234dcb9f3b28b0c9b5224)) + - (cargo-release) version 0.8.0 ([`1ccfdcd`](https://github.com/Byron/gitoxide/commit/1ccfdcdb96b59c6415e7fbc800371d594b2ef7a1)) + - (cargo-release) version 0.7.1 ([`2c38ff9`](https://github.com/Byron/gitoxide/commit/2c38ff909cd5ed39995d4ac3b5af3e0da2f3b76d)) + - (cargo-release) version 0.11.0 ([`1aa1f5e`](https://github.com/Byron/gitoxide/commit/1aa1f5e84a07427d5d7f3231735fe9c1923f506f)) + - Require latest version of git-features in git-odb ([`e664e93`](https://github.com/Byron/gitoxide/commit/e664e93e960564c43a5510d32bf5ff45624af8ee)) + - Remove usage of gitfeatures::fs in organize subcommand ([`b567d37`](https://github.com/Byron/gitoxide/commit/b567d3709a74e9fdafef54b0fe58ca82721cd773)) + - Refactor; planning ([`5df492c`](https://github.com/Byron/gitoxide/commit/5df492c7d7322bde2b268deaf590f1ba012a6b8e)) + - Thanks clippy ([`343ab9a`](https://github.com/Byron/gitoxide/commit/343ab9adb62da1dde495fc209c179137bbe59a10)) + - Refactor ([`5b1328f`](https://github.com/Byron/gitoxide/commit/5b1328fc48deab321f81d25b5dc8e9ba55840e2c)) + - Add missing '.' at end of doc comments ([`7136854`](https://github.com/Byron/gitoxide/commit/71368544f97369a4d371d43513607c4805bd0fd0)) + - Fix git-odb tests ([`35c1209`](https://github.com/Byron/gitoxide/commit/35c1209164b5baaa68d1c566344ac73ee6dfae79)) + - All crates use git-hash::Kind and its types, sometimes through git-object ([`124c171`](https://github.com/Byron/gitoxide/commit/124c171aaf546d8977e9913ff84e65383a80ee98)) + - Use git-hash in git-features ([`5b307e0`](https://github.com/Byron/gitoxide/commit/5b307e076f6f5975592c8b177c122c91c1d809c6)) + - (cargo-release) version 0.6.0 ([`27f5955`](https://github.com/Byron/gitoxide/commit/27f5955e047f35e21a86789eb46bfd89e1c99b44)) + - (cargo-release) version 0.9.0 ([`a89fdb9`](https://github.com/Byron/gitoxide/commit/a89fdb98f64bb0ca070fa79a1f58f1232bb14090)) + - (cargo-release) version 0.5.0 ([`fc7d600`](https://github.com/Byron/gitoxide/commit/fc7d600ac2c438c8b6b91f67cb69b0ac5ec37675)) + - (cargo-release) version 0.5.0 ([`c767e07`](https://github.com/Byron/gitoxide/commit/c767e07ccfc58a28e3e8ec22b590afdf0d92b9f2)) + - More docs for owned git-object ([`b79101d`](https://github.com/Byron/gitoxide/commit/b79101d714f59a42a30eb47776486a212ec0f738)) + - Thanks clippy ([`ba9b3c2`](https://github.com/Byron/gitoxide/commit/ba9b3c2345887353e02fc081be80733f1c5e22d9)) + - Refactor ([`d5d7cf9`](https://github.com/Byron/gitoxide/commit/d5d7cf9d3f42d83652a7b81bc6e1ee6731396d6b)) + - More docs of git-object::owned ([`0620dce`](https://github.com/Byron/gitoxide/commit/0620dce7a3ac368354c73e3927eb96a6e4990f0c)) + - (cargo-release) version 0.8.0 ([`47c00c2`](https://github.com/Byron/gitoxide/commit/47c00c2228cf25c79e1fa3eb4229c7ab24de91e5)) + - Cargo clippy Rust 1.48 ([`475a68c`](https://github.com/Byron/gitoxide/commit/475a68ce33b895de911939c51afa159df534f7b8)) + - Finish refactoring git-odb ([`ec282ae`](https://github.com/Byron/gitoxide/commit/ec282ae1a3d9f16eb9c89a44e17259112d097a41)) + - (cargo-release) version 0.7.0 ([`7fa7bae`](https://github.com/Byron/gitoxide/commit/7fa7baeb3e7d008a25e4d714eff908e2516c828b)) + - (cargo-release) version 0.4.2 ([`173c957`](https://github.com/Byron/gitoxide/commit/173c957032761705edc61a0ded1f963cac73c320)) + - Minor fixes to git-odb docs ([`3788512`](https://github.com/Byron/gitoxide/commit/37885125d7c4d1dba7aaff37b5d39a7c249bf794)) + - Complete docs for git-odb ([`0cf8496`](https://github.com/Byron/gitoxide/commit/0cf84967feed768bc29de29f65f6dc4622008464)) + - Prefer empty doc strings for modules over [allow missing docs] ([`9b3f04f`](https://github.com/Byron/gitoxide/commit/9b3f04f4247d6d2a139f813ea2555203d374962a)) + - Add remaining doc strings for git-odb ([`428f0ad`](https://github.com/Byron/gitoxide/commit/428f0ad2072148416b54b050add9a50868e7e5d0)) + - Some more docs ([`2d87124`](https://github.com/Byron/gitoxide/commit/2d87124344af845a34d17693f5ef04c9fb3323e1)) + - Try to document all the bits an pieces of git-odb ([`1b353fa`](https://github.com/Byron/gitoxide/commit/1b353fa95723a7fe4ddef0a70486a74957e727cd)) + - Finish docs on top-level traversal method ([`2ef1c99`](https://github.com/Byron/gitoxide/commit/2ef1c99a48c39cb9f3362a5ea493b5e90e4593c9)) + - Start describing how pack traversal works ([`5e990f2`](https://github.com/Byron/gitoxide/commit/5e990f20dee6005d23ebc5a56389f09d9d7f8782)) + - Refactor ([`a681335`](https://github.com/Byron/gitoxide/commit/a681335b51c10ff56ddd2fe80ec24449a771abd2)) + - Document pack::index::write ([`f5edc60`](https://github.com/Byron/gitoxide/commit/f5edc602cb3e570ce154a3ba3d692fcbcf8d55c0)) + - Dependency update ([`bc336d9`](https://github.com/Byron/gitoxide/commit/bc336d9bb22d13a6d2407b44b297fcb770cdaac6)) + - Refactor ([`6b909a2`](https://github.com/Byron/gitoxide/commit/6b909a22cf981b33060cb6f1324ec3231146d159)) + - Refactor ([`b511a2b`](https://github.com/Byron/gitoxide/commit/b511a2b1d9b6d55b1937ad3f4bbbb331b5cdd9a3)) + - Document index integrity checking ([`9336ab9`](https://github.com/Byron/gitoxide/commit/9336ab9f9675ba5d33eacefc585d745e1b0bcc18)) + - Docs for index access ([`996acbf`](https://github.com/Byron/gitoxide/commit/996acbf67fde183a0e5f553ecad9b2361eecf18b)) + - Docs for top level pack index module ([`d2dd72f`](https://github.com/Byron/gitoxide/commit/d2dd72fe2d230ecdd88343535ecdbfbd8ae1b143)) + - Document pack data verification ([`27962ca`](https://github.com/Byron/gitoxide/commit/27962ca9019d0b4971fa76afedaf1d85f451665b)) + - Document pack entry iteration ([`c869ee9`](https://github.com/Byron/gitoxide/commit/c869ee93c6f042ce3de4962229e2caa4377af62b)) + - Docs for pack header ([`9505b40`](https://github.com/Byron/gitoxide/commit/9505b401a87c3107ac1e5775ff6c10e8a808ba25)) + - Some more pack data file documentation ([`05e05f4`](https://github.com/Byron/gitoxide/commit/05e05f46a38bcc068b564409d92310dd93ca5527)) + - Docs for Bundle::write_* ([`ac41253`](https://github.com/Byron/gitoxide/commit/ac41253067803796e5623184d7dee790aa597809)) + - Remove special Error with just one variant… ([`d05a416`](https://github.com/Byron/gitoxide/commit/d05a416dc43164f4c9fb2ee00884107fdbd13f90)) + - Docs for Bundle::locate ([`066787c`](https://github.com/Byron/gitoxide/commit/066787c12e3142732d3ba65b233c836f89745543)) + - Some more docs for 'pack' module ([`c32850d`](https://github.com/Byron/gitoxide/commit/c32850d4b6f94dd636d09b6222d2aa7ee6a85c82)) + - Some more documentation ([`201f67c`](https://github.com/Byron/gitoxide/commit/201f67ce52e39dde3a79ff8a1f05bbaf30deec15)) + - Merge branch 'commit-graph' into main ([`9cb09b2`](https://github.com/Byron/gitoxide/commit/9cb09b248796f0ff5c9d3f3e857de4731324cfd5)) + - Specify the hash to create with 'hash::bytes_of_file' ([`c000294`](https://github.com/Byron/gitoxide/commit/c000294423ae0759b978399db3b69ac07c20578d)) + - Move 'git_odb::hash::bytes_of_file' into git_features::hash ([`c5f6b45`](https://github.com/Byron/gitoxide/commit/c5f6b4587ee4042a080c0505613b0c72fdfe5273)) + - The daily commit (single handedly) ([`b528c2e`](https://github.com/Byron/gitoxide/commit/b528c2e1bf0a3211491535427c4bd36212711a0f)) + - Document `loose::Object` entirely ([`d5eef9c`](https://github.com/Byron/gitoxide/commit/d5eef9cdd06910eeaf1f1c4114b97638a29c7327)) + - Thanks clippy ([`b9e0a87`](https://github.com/Byron/gitoxide/commit/b9e0a87996b8f3c4531a392607c353a1f0824ce6)) + - Docs for Sink ([`e7a09f0`](https://github.com/Byron/gitoxide/commit/e7a09f0628b44ae0c6b564ef41f044e51866f2df)) + - Docs for compound object databases ([`813df71`](https://github.com/Byron/gitoxide/commit/813df7115eb643742158f975975eb7469443cc07)) + - Document borrowed odb objects ([`7626f7f`](https://github.com/Byron/gitoxide/commit/7626f7f3af885f1b95801f9dbc71bee0bc77400e)) + - Document alternates implementation ([`60666e8`](https://github.com/Byron/gitoxide/commit/60666e86316c81f3bb63ee151e457af78dbefc00)) + - Docs for git-odb crate (top-level) ([`71af366`](https://github.com/Byron/gitoxide/commit/71af366c84e1bd00125b4582d80799a6d927324a)) + - Remove dash in all repository links ([`98c1360`](https://github.com/Byron/gitoxide/commit/98c1360ba4d2fb3443602b7da8775906224feb1d)) + - Merge branch 'main' into commit-graph ([`ca5b801`](https://github.com/Byron/gitoxide/commit/ca5b80174b73cc9ac162b3f33b5d3721ef936cb1)) + - Thanks clippy ([`e355b4a`](https://github.com/Byron/gitoxide/commit/e355b4ad133075152312816816af5ce72cf79cff)) + - Refactor ([`5a1cbf2`](https://github.com/Byron/gitoxide/commit/5a1cbf299f2d5c1c07143d14ee3ded95d6a44a20)) + - And octal values unquoting works too ([`5effc7b`](https://github.com/Byron/gitoxide/commit/5effc7b6daf6ff49b6d51af09f8da148602c7322)) + - All explicit escapes ([`1841544`](https://github.com/Byron/gitoxide/commit/18415445caaee6e9e54aabddb88bdcd2f5602508)) + - First bunch of simple unescapes ([`a45c594`](https://github.com/Byron/gitoxide/commit/a45c5941cf426537710842917c0e715cf4c74863)) + - Prepare for actual unescaping ([`284da44`](https://github.com/Byron/gitoxide/commit/284da449cae62d12ea4eea8c31f1225699c5e52e)) + - Basic infrastructure for unquoting c-style strings ([`f81bb03`](https://github.com/Byron/gitoxide/commit/f81bb038bfc8ea0d9b61012d6effae084c89335a)) + - Fix incorrect cycle detection, which worked on MacOS by accident ([`a6e7765`](https://github.com/Byron/gitoxide/commit/a6e77650a886ac33b23af8892182c9832a86e997)) + - Also use alternates for looking up objects… ([`ade929d`](https://github.com/Byron/gitoxide/commit/ade929df38e619850f73389178a2c53e1c43fa45)) + - Prepare for unquoting c-strings ([`47e2fa0`](https://github.com/Byron/gitoxide/commit/47e2fa03a1e2fe163c5c019d52bbb0ddbdb80bf0)) + - Read multiple alternates from single file; ignore comments ([`1f8d367`](https://github.com/Byron/gitoxide/commit/1f8d36705c4568b1036b0d62b3a80ae6ec20a99c)) + - Support for relateive alternates ([`b20e9ee`](https://github.com/Byron/gitoxide/commit/b20e9eea423ced275781d410217110c85ddb587c)) + - Ignore all cycles and be happy if we have found at least one actual odb ([`1effdfd`](https://github.com/Byron/gitoxide/commit/1effdfda703d5eb9cd1333a7bae21075ef9e53cc)) + - Prepare for multi-line parsing and all the bells and whistles ([`08f9ec4`](https://github.com/Byron/gitoxide/commit/08f9ec41feee56fe0ff2b057bb50391100bdb84e)) + - Make compound DB initialization less lazy… ([`6dc57b3`](https://github.com/Byron/gitoxide/commit/6dc57b31d0bc5abfca100ab1d4b5dff68852aad8)) + - Use parallel walkdir (via jwalk) when parallel feature is enabled ([`f444c85`](https://github.com/Byron/gitoxide/commit/f444c859f5b215ea70a46d5493a2babbf7a98235)) + - Alternate now handles cycles ([`71167e4`](https://github.com/Byron/gitoxide/commit/71167e4e50efa8a097c3b09a249004e97aeaf2c8)) + - First simple alternate tests ([`7372118`](https://github.com/Byron/gitoxide/commit/73721185cfd646c6e83b2548280fad8f480f8324)) + - Test for circular alternates ([`fc92709`](https://github.com/Byron/gitoxide/commit/fc927091d69196a930c0cea4611af8d96b7b84d8)) + - Thanks clippy ([`4ddc64f`](https://github.com/Byron/gitoxide/commit/4ddc64fd71d3d1260e001f89c379c46fe157e5ce)) + - Actually resolve alternates when creating a compound DB ([`9be7aed`](https://github.com/Byron/gitoxide/commit/9be7aed7bd4b939d98b9a8d1db8a6ffc85044ca9)) + - Refactor ([`c1eff58`](https://github.com/Byron/gitoxide/commit/c1eff58cd28e45a2d5f46481551724b81735ede3)) + - First sketch of alternate resolution ([`6cc8a94`](https://github.com/Byron/gitoxide/commit/6cc8a947df776aeeb031de627f84b7bc85207235)) + - Remove quickerror dependency from git-odb ([`7e27495`](https://github.com/Byron/gitoxide/commit/7e2749521b6c873766a2f6f96e6c91a0c6a9dbf3)) + - Refactor ([`7874c35`](https://github.com/Byron/gitoxide/commit/7874c35bccb74ae7670335e633efa7eaebc72630)) + - Refactor ([`3ec99dc`](https://github.com/Byron/gitoxide/commit/3ec99dc7360c01b4f3c4593ff5049361e7043254)) + - Refactor ([`519dd12`](https://github.com/Byron/gitoxide/commit/519dd12f2bf58dd3048bc12e5b058236ad727853)) + - Refacator ([`7ac2153`](https://github.com/Byron/gitoxide/commit/7ac21536b3cee6708489011731216b9b831509e4)) + - Refactor ([`d4f288c`](https://github.com/Byron/gitoxide/commit/d4f288ceb2436b292993df74ed07d4d7e578edf1)) + - Refactor ([`3a8fb61`](https://github.com/Byron/gitoxide/commit/3a8fb61067c210d4db6d515f21b2e28425c52e8c)) + - Refactor ([`98b3f4a`](https://github.com/Byron/gitoxide/commit/98b3f4a9cc65e76aa09280065ab1d151f637e692)) + - Refactor ([`127b8b2`](https://github.com/Byron/gitoxide/commit/127b8b2949476b38ef6f8ea0fb68bae6d473adcc)) + - Refactor ([`669b726`](https://github.com/Byron/gitoxide/commit/669b726da305ce4520c792d62b4344b04fe5f996)) + - Refactor ([`7bc321e`](https://github.com/Byron/gitoxide/commit/7bc321e96ecce0aae5063eb7008ecbac7d2ca31c)) + - Refactor ([`0752b45`](https://github.com/Byron/gitoxide/commit/0752b45e95dd5378b7fca5b70bd11b9100ba2946)) + - (cargo-release) version 0.4.1 ([`60ac8b0`](https://github.com/Byron/gitoxide/commit/60ac8b0a7545fff6ef293fd48716e9a19175517c)) + - Refactor ([`ad17bfd`](https://github.com/Byron/gitoxide/commit/ad17bfdc07e1301693fdfa3d09df3b39f675a36f)) + - Merge from main. ([`b59bd5e`](https://github.com/Byron/gitoxide/commit/b59bd5e0b0895c7d1d585816cec8be4dea78c278)) + - Refactor ([`91d9f78`](https://github.com/Byron/gitoxide/commit/91d9f78a9af04b44b2cead30d6e1cbaaeb76a522)) + - Refactor ([`6ebb5d1`](https://github.com/Byron/gitoxide/commit/6ebb5d1839cd5ab4d8aff78acbccebaa66f439c7)) + - Refactor ([`8877b77`](https://github.com/Byron/gitoxide/commit/8877b776bda8d1f202e86ac471ea30b595cff41b)) + - Refactor ([`4a0d034`](https://github.com/Byron/gitoxide/commit/4a0d0342a20f519f30fe8b84d51ebb2bdea23752)) + - Refactor ([`485aa91`](https://github.com/Byron/gitoxide/commit/485aa91c7412c55c0215e33cc4f906dd62e440a8)) + - Refactor ([`c1d2f41`](https://github.com/Byron/gitoxide/commit/c1d2f41938211985a6cdb7a0fde6bcb51a7944de)) + - Refactor ([`07aff14`](https://github.com/Byron/gitoxide/commit/07aff14a8c2ceca3202b0506b3bd4286550ac3a0)) + - Refactor ([`57d463f`](https://github.com/Byron/gitoxide/commit/57d463ffeb5861270abaaf72f662b14c9c262052)) + - Refactor ([`c6be43d`](https://github.com/Byron/gitoxide/commit/c6be43de3493566cedd98ce49fb2c8af7714a61c)) + - Refactor ([`524d0fe`](https://github.com/Byron/gitoxide/commit/524d0fec17c356c846f0c62f87f2637a7a6fa56b)) + - Refactor ([`a8f4cd7`](https://github.com/Byron/gitoxide/commit/a8f4cd7b9c31e59c3329cc649aca8378cd34a597)) + - Checksum verification for compound object ([`3be08b0`](https://github.com/Byron/gitoxide/commit/3be08b09cd71e5e5eb21bdd81d6a07d2c232e6e8)) + - Refactor ([`59d989a`](https://github.com/Byron/gitoxide/commit/59d989a9c86789d6572c9a3dfd8a3652bd4a7a1b)) + - More methods for compound object ([`84d2b0e`](https://github.com/Byron/gitoxide/commit/84d2b0ec53f7def1470fbadff45fbe266bceb71a)) + - Refactor ([`e5a9343`](https://github.com/Byron/gitoxide/commit/e5a9343f3f5304de4c9f614cdb260cf0fcfbbbfb)) + - Refactor ([`6a84f13`](https://github.com/Byron/gitoxide/commit/6a84f137754cddfdcb9b1fec3e353762ebb3ce2b)) + - Refactor ([`4e89c3b`](https://github.com/Byron/gitoxide/commit/4e89c3bc0f14cf9581348ae2c1620ade9dc1db8f)) + - Document why we won't use nightly for fixing NLL issue ([`ca29368`](https://github.com/Byron/gitoxide/commit/ca29368b42b902fe7fe14dd4bff1b35e266c96a8)) + - Revert "Fix NLL issue by using nightly" ([`6864a55`](https://github.com/Byron/gitoxide/commit/6864a55001f1d01839f948618355928d666602ee)) + - Fix NLL issue by using nightly ([`8c5bd09`](https://github.com/Byron/gitoxide/commit/8c5bd095539042d7db0e611460803cdbf172beb0)) + - Update tasks, prepare for NLL fix ([`52af8d1`](https://github.com/Byron/gitoxide/commit/52af8d1089dc85cff19aee276bd831393f1b84b3)) + - Thanks clippy ([`6c4d1ec`](https://github.com/Byron/gitoxide/commit/6c4d1ec33d37b99b38698dfd91d38216ab4a2ef2)) + - This works, but locates twice… ([`4e709f6`](https://github.com/Byron/gitoxide/commit/4e709f6029cf98f8c6ff204598706e2b6a1775eb)) + - Also the imperative version doesn't borrowcheck… ([`c5720f1`](https://github.com/Byron/gitoxide/commit/c5720f1e4dc790539980fa81e940be6c6e15b50a)) + - Looks like the functional approach to locate(…) doesn't borrowcheck ([`5df6867`](https://github.com/Byron/gitoxide/commit/5df6867a2b9fa7ba3fe2cdcd3bb9766faba1ae1b)) + - Refactor ([`9e68c6b`](https://github.com/Byron/gitoxide/commit/9e68c6bcd1d07ea73730ce5ff59d7883152f894d)) + - Refactor ([`f219d5a`](https://github.com/Byron/gitoxide/commit/f219d5a25efb7e258249ca3a4d39382136fe4229)) + - Sketch compound::Db::locate; sort packs by size ([`6609a53`](https://github.com/Byron/gitoxide/commit/6609a534f45fc1ffc9d80a60a6a9793cbf54131c)) + - Refactor ([`4a09754`](https://github.com/Byron/gitoxide/commit/4a09754f6cd17d7f39f8a71b7de44d517294ffc5)) + - Implement Write in terms of writing to the loose object DB ([`02b88c2`](https://github.com/Byron/gitoxide/commit/02b88c28304ff6d8c1fbad6fdcfa36f3b1f9dafc)) + - First sketch of compound Db ([`9bf2279`](https://github.com/Byron/gitoxide/commit/9bf227914d9281bfbdfc902edc3c1cc21c7fa3cd)) + - Refactor ([`203ba99`](https://github.com/Byron/gitoxide/commit/203ba995c9e237ac63bc2ecebda18171e90fcb47)) + - (cargo-release) version 0.6.0 ([`9ef184e`](https://github.com/Byron/gitoxide/commit/9ef184e35712f938fb4f9f6da7390a8777a9284e)) + - Revert "FAIL: try to get rid of tree-traversal Boxed error…" ([`1b42b31`](https://github.com/Byron/gitoxide/commit/1b42b3141dded644a17c8d23057c987e2bac4f80)) + - Try to get rid of tree-traversal Boxed error… ([`13159eb`](https://github.com/Byron/gitoxide/commit/13159eb972ed78ce4ebee2313b288023cec91c47)) + - Parameterize traversal error with Processor error ([`1513a13`](https://github.com/Byron/gitoxide/commit/1513a13179bedefd12fc08da07a05c7f07ed4ef6)) + - Switch to prodash 10 and safe a lot of trait bounds in the process ([`e2fb1d9`](https://github.com/Byron/gitoxide/commit/e2fb1d944b4d803a11c91f868b831d406fb5e35f)) + - (cargo-release) version 0.4.0 ([`2272fa4`](https://github.com/Byron/gitoxide/commit/2272fa4bcacdaf1898e4cd8b791232fc1321227f)) + - (cargo-release) version 0.4.0 ([`0d7b60e`](https://github.com/Byron/gitoxide/commit/0d7b60e856325009431172e1df742a1cd2165575)) + - (cargo-release) version 0.5.0 ([`82b7313`](https://github.com/Byron/gitoxide/commit/82b73131b79ec3c42a712dad1c0766a72209d737)) + - [clone] All it took was a an intermediary to call 'read' as expected ([`7c8ecb7`](https://github.com/Byron/gitoxide/commit/7c8ecb78e988f7752cea6fe2022ccf9739b86fd4)) + - [clone] minor refactor; it's definitely the read() that doesn't work… ([`406829b`](https://github.com/Byron/gitoxide/commit/406829b951164673c0b8152d1e9de76f1318df0a)) + - [clone] This actually works: first MVP of retrieving packs via clone ([`c06d819`](https://github.com/Byron/gitoxide/commit/c06d8194173f9ec468ddd0faf72dd6d8dbf7d35d)) + - [clone] support for progress that can handle writing pack files ([`46e0055`](https://github.com/Byron/gitoxide/commit/46e0055eab47e402807b15c63b6a4577f5c0b7bb)) + - Use fast-reset for miniz oxide to gain about 4s when resolving the kernel pack ([`e5b6ce4`](https://github.com/Byron/gitoxide/commit/e5b6ce440073c1db32ed4afc8e1db21b32f62863)) + - Fix build ([`6178133`](https://github.com/Byron/gitoxide/commit/6178133fd1e08af6abb90ba7d1a4c22970da850c)) + - Refactor ([`174baa7`](https://github.com/Byron/gitoxide/commit/174baa7733d34d1dbb2d47f4163ca39fb4a4c473)) + - Bump git-features to 0.4 to allow publishes after breaking changes ([`9d6b879`](https://github.com/Byron/gitoxide/commit/9d6b8790e2edd7fa01b3239adff86a7cd2393f10)) + - Allow dual-licensing with Apache 2.0 ([`ea353eb`](https://github.com/Byron/gitoxide/commit/ea353eb02fd4f75508600cc5676107bc7e627f1e)) + - Refactor ([`b4a6e16`](https://github.com/Byron/gitoxide/commit/b4a6e16364822c0dccb56f98dbfb0ca4c8007069)) + - Remove tree compaction code ([`dfc6c7d`](https://github.com/Byron/gitoxide/commit/dfc6c7dde9014e79eb4a752d81bc3c77ad36c366)) + - See if tree compaction saves considerable amounts of memory ([`0092c25`](https://github.com/Byron/gitoxide/commit/0092c256b3bfaf2818566540e660cdefcf68d246)) + - Bump minor version to 0.3 ([`4351e28`](https://github.com/Byron/gitoxide/commit/4351e2871c9dcf342b8471fffa74cae338a53269)) + - Thanks clippy ([`6725104`](https://github.com/Byron/gitoxide/commit/6725104d2841e6518db641d06e3e107cf4f40f96)) + - Also run file hashing in indexed mode in parallel (like with lookup) ([`8f8d14f`](https://github.com/Byron/gitoxide/commit/8f8d14f4606e99dc710eb352a985db48c00ea4f4)) + - First step towards parallelizing file hashes and traversal! ([`9573836`](https://github.com/Byron/gitoxide/commit/95738369e0d3accf7f6239c8cd966a7f5c36825a)) + - Allow hashing to be interrupted ([`df4dfd7`](https://github.com/Byron/gitoxide/commit/df4dfd7ec1be608cec1117f56303c528fb8f7ba7)) + - Unify file based file verification of data and index ([`e1b4105`](https://github.com/Byron/gitoxide/commit/e1b4105308963cfe9102c2dee461c7dd948ee942)) + - Update to quick-error 2.0 ([`4b1b784`](https://github.com/Byron/gitoxide/commit/4b1b7849b47a54092b49821c39e864c86adda979)) + - Haha, funny, silly me… ([`a4a1244`](https://github.com/Byron/gitoxide/commit/a4a1244af2d386e75ebd55909d4675b475ccd905)) + - Better usability for units ([`b226253`](https://github.com/Byron/gitoxide/commit/b226253636d8146a084a7bcd7c0c320e37f9d2fb)) + - Better progress for Sha1 of pack and index ([`310a59e`](https://github.com/Byron/gitoxide/commit/310a59ee99ce78a4f14326c0058ea0c543a1d24c)) + - Make obvious that interrupt request was received ([`34b2373`](https://github.com/Byron/gitoxide/commit/34b23737f560fe52d4f98fb886eba754652f9a5e)) + - Conditionally use an eager iterator… ([`e9b5511`](https://github.com/Byron/gitoxide/commit/e9b5511568f4e64968596994855783f19672d678)) + - Reduce progress information for threads ([`e9a1b31`](https://github.com/Byron/gitoxide/commit/e9a1b310fd99675dc87e56c6277d57259a6415a0)) + - Revert "A test to see how much time can be saved by not zeroing zlib buffers" ([`3d51d59`](https://github.com/Byron/gitoxide/commit/3d51d595469d8451867331089e75b808f9361912)) + - A test to see how much time can be saved by not zeroing zlib buffers ([`fd41a51`](https://github.com/Byron/gitoxide/commit/fd41a51de2261f425262ee7dee7a24fd87d87432)) + - Implement optionally keeping the compressed bytes ([`fc26914`](https://github.com/Byron/gitoxide/commit/fc26914a57b6e89c703e1b04d6a4d8d31005ddbc)) + - First step towards more control over allocation in iterator ([`cacf76c`](https://github.com/Byron/gitoxide/commit/cacf76cd69996073894e400e65322d3547493789)) + - Never keep decompressed bytes while streaming… ([`65c3856`](https://github.com/Byron/gitoxide/commit/65c38569219134ccd412a8a3ee7ec618d866c941)) + - Only keep base objects, not the deltas (like originally intended) ([`fc8334b`](https://github.com/Byron/gitoxide/commit/fc8334b8d196425f2b766ebb9772a12483ef0f8d)) + - Reduce footprint of sha1 when writing the index ([`12aa454`](https://github.com/Byron/gitoxide/commit/12aa4549bee0d9ea00bb0723acefa8187f5119a9)) + - First successful test of moving the streaming iterator into its own thread ([`c9fcb68`](https://github.com/Byron/gitoxide/commit/c9fcb68c644c96a15cb9956a754bec7b65bb5fbd)) + - First sketch of order-destroying eager iterator ([`20fca45`](https://github.com/Byron/gitoxide/commit/20fca4515f6e9ea320d0bf21c15cd6d2c3cff742)) + - Add object size progress when resolving with index ([`b2f8c9e`](https://github.com/Byron/gitoxide/commit/b2f8c9e85dfac63f70ca7b0e91af697b801b4131)) + - Add decompression progress ([`0e5c534`](https://github.com/Byron/gitoxide/commit/0e5c534d7c6e661a1f6c1cdb59ad1c9ffade642d)) + - Print read throughput automatically ([`0a71b48`](https://github.com/Byron/gitoxide/commit/0a71b482310a129aa8757475290b3b24a200b702)) + - Allow 'read' progress to go out of scope while keeping it accessible! ([`d7a7828`](https://github.com/Byron/gitoxide/commit/d7a782899ca841291e240bad822bb8184d6f5083)) + - Fix throughput display of otherwise stepped progress indicators ([`399f81d`](https://github.com/Byron/gitoxide/commit/399f81daadb8c111b9cad958945924e0eed2c2ad)) + - Unify used ranges for line renderer amond pretty and lean interface ([`f59f66e`](https://github.com/Byron/gitoxide/commit/f59f66e189732f567414f68c7463364e510f41c4)) + - Add percentage and throughput to tasks that matter ([`763d7ca`](https://github.com/Byron/gitoxide/commit/763d7caa4c70111b7cb3ef5733d2c3c697758c28)) + - Upgrade to latest iteration of prodash ([`3a4faec`](https://github.com/Byron/gitoxide/commit/3a4faecab56e37670c553e6563f11a46d740c333)) + - First part of migration to prodash 8.0, but… ([`6901a09`](https://github.com/Byron/gitoxide/commit/6901a098641820c8d974ce56a24d6cdca779730d)) + - Fix various issues related to 64bit offset support when writing indices… ([`da31694`](https://github.com/Byron/gitoxide/commit/da31694ee13022bcc52ed06389469d65b4e37daa)) + - Fix unit tests: actually sort the directory entries :D ([`b69717a`](https://github.com/Byron/gitoxide/commit/b69717af368510347a550012f4ed97ba24d36ffd)) + - Add convenience method to get a new bundle for the index/data just written ([`a6d74ad`](https://github.com/Byron/gitoxide/commit/a6d74ad7b65cdc293c8504dae73ea1c717e5bfca)) + - Bundle write with a given directory ([`7f29c73`](https://github.com/Byron/gitoxide/commit/7f29c73d35b8717c8834beac259ed71eebcc2058)) + - First unit test for bundle writing ([`74bda39`](https://github.com/Byron/gitoxide/commit/74bda3963af7fe4b97e7f04f0bb9e150df8b7fa7)) + - Journey tests for restore functionality ([`1aa63e4`](https://github.com/Byron/gitoxide/commit/1aa63e419736960915c03c29827a57c18261e04d)) + - Refactor ([`fc42567`](https://github.com/Byron/gitoxide/commit/fc4256788f7c3d3c4a05f240eee4d71a716cafce)) + - Refactor ([`cf3ebe0`](https://github.com/Byron/gitoxide/commit/cf3ebe00619d16e957166578038520b2bf080411)) + - Refactor ([`72ca435`](https://github.com/Byron/gitoxide/commit/72ca435a90e470797ae59dd10640c36b84bb4f41)) + - More flexible error types for processors - anything goes ([`be3a947`](https://github.com/Byron/gitoxide/commit/be3a947ba6197319fea0b38e48008850cc971bf6)) + - Refactor ([`c7dd581`](https://github.com/Byron/gitoxide/commit/c7dd581348a05146d7a79f7622bf30a08d34f474)) + - Refactor ([`aae8e79`](https://github.com/Byron/gitoxide/commit/aae8e79a89261a548f088454ca6082a34c2063ce)) + - Refactor ([`0e27763`](https://github.com/Byron/gitoxide/commit/0e27763995e135fc1bca56e6084b5c81825dba22)) + - Make lookup based algorithm gracefully interruptible ([`8d2e649`](https://github.com/Byron/gitoxide/commit/8d2e649c754d713e6dd48315cd043204ffda4a7b)) + - Write about user interfaces and the use/non-use of async ([`91ba045`](https://github.com/Byron/gitoxide/commit/91ba0457745f860b7a68cb38b13e69754747e8d9)) + - Use pack hash for index file as well :D ([`2106c64`](https://github.com/Byron/gitoxide/commit/2106c64484c2162ee4e715efc592db14da602327)) + - Support for interruptible operations ([`a025593`](https://github.com/Byron/gitoxide/commit/a02559378f9165df97a217f24834a851be719b08)) + - Thanks clippy ([`62d2ff3`](https://github.com/Byron/gitoxide/commit/62d2ff383c5f7fe884057c70868569a811a73e00)) + - Organize object type comparisons by probability… ([`19a5d94`](https://github.com/Byron/gitoxide/commit/19a5d9465f7962cfcc39ea31a2c84be6235e40ed)) + - Count object types as well ([`e04a8d1`](https://github.com/Byron/gitoxide/commit/e04a8d16fda3712663d8d9220f3a017e668b6283)) + - Revert "Less memory for look up mode, faster start" - too slow ([`584350a`](https://github.com/Byron/gitoxide/commit/584350af91f533db4cf980327d530445384c6b5a)) + - Less memory for look up mode, faster start ([`395c7e7`](https://github.com/Byron/gitoxide/commit/395c7e78ef344ee56cf3d4ef49828942a09094bc)) + - Remove petgraph entirely ([`70ba33a`](https://github.com/Byron/gitoxide/commit/70ba33a23a3ef887323ee29c248422f1997af6be)) + - Compute statistics for indexed pack verify ([`3d31c23`](https://github.com/Byron/gitoxide/commit/3d31c235edaf7f88eb954cffc6864777566b3ef1)) + - Prepare for computing indexed statistics ([`082c246`](https://github.com/Byron/gitoxide/commit/082c2467f2ab46aeb285504abcf2d8945dac4ce5)) + - Refactor ([`bfbae90`](https://github.com/Byron/gitoxide/commit/bfbae905d3e8a0c5f30779c1723163a947de355e)) + - Keep all metadata per object needed to compute the usual statistics ([`961b85e`](https://github.com/Byron/gitoxide/commit/961b85efec1ce84beacaa35720746752f687413a)) + - Make 'level' available to support statistics ([`f7ba51c`](https://github.com/Byron/gitoxide/commit/f7ba51c93b04ef2e98f2436cf72e8c28b89b2448)) + - Refactor ([`6277318`](https://github.com/Byron/gitoxide/commit/6277318f2ea71451b023a11fc9f74149d11fe9a9)) + - Support for error handling in traversal callbacks ([`c1d5bf6`](https://github.com/Byron/gitoxide/commit/c1d5bf628db5f0c79aaf9af9740b990fc78aa4d5)) + - Indexed traversal now works, in theory, but needs error handling ([`86f8400`](https://github.com/Byron/gitoxide/commit/86f8400a5e74e75fe7dab24911215a3f820b64b1)) + - Support for progress ([`62108fd`](https://github.com/Byron/gitoxide/commit/62108fda164ae35903147eb1808c951bb90dac85)) + - Support for thread local storage in callbacks ([`1dad088`](https://github.com/Byron/gitoxide/commit/1dad088a7d0be10a83cae0f119d42501887043e3)) + - Support for learning about the objects slice in the pack ([`faec782`](https://github.com/Byron/gitoxide/commit/faec78276c4814edc7bbde150f8379fa73abc364)) + - And even more caapbilities are required to make tree traversal work natively ([`90523bb`](https://github.com/Byron/gitoxide/commit/90523bb983e2cac70dad822531b7d66b7196cefc)) + - Refactor ([`2bbfd82`](https://github.com/Byron/gitoxide/commit/2bbfd82f909ebc30cfb276bf40c7dbaa424a62f8)) + - Refactor ([`efa7cd8`](https://github.com/Byron/gitoxide/commit/efa7cd843f7e93a8c4beba20597ff6d914bd6a33)) + - First steps towards actually using the new tree traversal during verification ([`785b0ff`](https://github.com/Byron/gitoxide/commit/785b0ff02c0e00f6e7dea3a9c41a32f4129659e6)) + - Thanks clippy ([`44b20de`](https://github.com/Byron/gitoxide/commit/44b20deafeac85151d57ecf4c0f5d889e9fe32f7)) + - Refactor ([`afe5e44`](https://github.com/Byron/gitoxide/commit/afe5e445617c79b29d519257042b85d9533d40b0)) + - Refactor ([`fcc660d`](https://github.com/Byron/gitoxide/commit/fcc660dee6f70b40364c70c73fc6b436929df4cd)) + - Reduce memory usage for index considerably ([`aa802be`](https://github.com/Byron/gitoxide/commit/aa802be3402ad26a2907711cd5d1476b0caeec03)) + - And now it works! ([`f14e10e`](https://github.com/Byron/gitoxide/commit/f14e10e9cfe1f4a4b477fcfc9459e49b439b0217)) + - Use new traversal in index writing, but it doesn't work yet ([`0dd5570`](https://github.com/Byron/gitoxide/commit/0dd5570a1c615192f3c9382dfb7ffb1d817924db)) + - Refactor ([`4ff69c6`](https://github.com/Byron/gitoxide/commit/4ff69c6281ba8d9af29a9f4407e9b2fa72f6550c)) + - Refactor ([`6cbb7cc`](https://github.com/Byron/gitoxide/commit/6cbb7ccfc3e1fde4febfe652c25f5566937d3ad2)) + - Generalized tree traversal can theoretically work ([`64158e0`](https://github.com/Byron/gitoxide/commit/64158e095f348ffa15139a9fa586074dad4d648b)) + - Make traversal part of the tree for greater ease of use ([`6629e30`](https://github.com/Byron/gitoxide/commit/6629e3043786e5caf7d2b6fedc9350cd9e7bc6fb)) + - Prepare flexible traversal on decompressed objects ([`7707ea6`](https://github.com/Byron/gitoxide/commit/7707ea6cf99d5ee93e4d6eea57adf00190d79d87)) + - Refactor ([`deea36c`](https://github.com/Byron/gitoxide/commit/deea36c090fdd57ef8fc900744bbf17bd6e70097)) + - Refactor ([`83a0102`](https://github.com/Byron/gitoxide/commit/83a01024d324123234776c8200ec3a3ae5f3c54e)) + - Refactor ([`b77d148`](https://github.com/Byron/gitoxide/commit/b77d148ed1c5aec31cb0493b4f1e0f2d82d7e641)) + - Generalize tree iteration ([`fdc06de`](https://github.com/Byron/gitoxide/commit/fdc06de2af8e7c9d2000177ce4f99ac68b5335be)) + - Also index using the new tree impl during verify (prepare replacement) ([`92039b0`](https://github.com/Byron/gitoxide/commit/92039b038653cf97029e06f3f9b80892035d8c87)) + - Refactor ([`e3ff6af`](https://github.com/Byron/gitoxide/commit/e3ff6af014cfbdbb53fe9498ff75b7f49fa5beb7)) + - Support for building a tree from offsets ([`95858bc`](https://github.com/Byron/gitoxide/commit/95858bcbad01138240512731ec0e6dbdaed6c9fe)) + - Refactor ([`8cfe025`](https://github.com/Byron/gitoxide/commit/8cfe0257de05b08a1278f78f6bdf3b5d65447686)) + - Refactor ([`bb9e518`](https://github.com/Byron/gitoxide/commit/bb9e518b71ee1b4e1ab24d1369b879e047009294)) + - Count sorting into the progress, 7.5 mio entries takes a moment ([`2fc4cd8`](https://github.com/Byron/gitoxide/commit/2fc4cd8dcac50f21491b5d297237acf97b2759fa)) + - Use bigger buffers when reading from disk. ([`e76e4eb`](https://github.com/Byron/gitoxide/commit/e76e4ebb2261351bfe2af42b5782f0058f15edc6)) + - Only keep decompressed bytes of base objects… ([`b39ad89`](https://github.com/Byron/gitoxide/commit/b39ad8976ee853229f87bbf962ada9557c7bbd32)) + - Remove memory mode entirely (and some complexity with it) ([`8812e91`](https://github.com/Byron/gitoxide/commit/8812e916a21983868a37c4aade10f79a1dc9b926)) + - Turns out you never want to keep deltas in memory ([`657aa2c`](https://github.com/Byron/gitoxide/commit/657aa2c38673cf10174f42bcb97039ac37b2926e)) + - Remove support for keeping compressed memory to reduce the index size ([`1e2ec7e`](https://github.com/Byron/gitoxide/commit/1e2ec7e9d0ef2f2a4908860672080e411e945bff)) + - Don't cause re-allocs of the compression buffer ([`2bb6fd2`](https://github.com/Byron/gitoxide/commit/2bb6fd26235825484a8f60a49455fee71f08236c)) + - Revert "FAIL: try to use a customized version of just pieces of Miniz-oxide" ([`ea0fdb3`](https://github.com/Byron/gitoxide/commit/ea0fdb3c9ae42fcbd97f9319e90873c053d4ab71)) + - Try to use a customized version of just pieces of Miniz-oxide ([`9945eba`](https://github.com/Byron/gitoxide/commit/9945eba749afb020e0deaaa5bb01fda6ff9ccd84)) + - Dependency upgrade + update ([`c6692c6`](https://github.com/Byron/gitoxide/commit/c6692c6d494fe2bf1f9b924cf27da5908b74d62b)) + - Refactor ([`133e3ba`](https://github.com/Byron/gitoxide/commit/133e3bafea772028f4bfd0fcc28a3e9bc3507701)) + - Let go of another handbreak - decompression is much faster now ([`ae9dc16`](https://github.com/Byron/gitoxide/commit/ae9dc165b72893216e7337bf0726705adce69cd8)) + - Thanks clippy ([`393067d`](https://github.com/Byron/gitoxide/commit/393067ddcf19424381ad2703c9c987d0f99587cd)) + - Use call to produce the resolver, allowing to delay opening a file mapping… ([`dd30e8d`](https://github.com/Byron/gitoxide/commit/dd30e8d3c8b6754bd90e2777ec0153e158d4a708)) + - Fix actual memory violation (thanks to unsafe code) ([`c44c5e1`](https://github.com/Byron/gitoxide/commit/c44c5e1890bc26ced920eb484e8708456d69df15)) + - Thanks clippy ([`1083a0b`](https://github.com/Byron/gitoxide/commit/1083a0b75298454d19c2bdabaf0e195c78543792)) + - Reduce memory consumption ([`6d1a7a1`](https://github.com/Byron/gitoxide/commit/6d1a7a1292e8065d0a777cb6acd34776b1e23696)) + - Unfortunately working with an optional for data is unwieldy, let's use default ([`12bbca0`](https://github.com/Byron/gitoxide/commit/12bbca0b2dd780c3f6d4117a6bd0420fec0823bc)) + - Tree can now be used as sole data structure, collecting all results ([`3e52d6f`](https://github.com/Byron/gitoxide/commit/3e52d6f89cb0ff0ab7e5a7fdb5aa892b498eef29)) + - Preparation for allowing reuse of the tree data structure ([`f565512`](https://github.com/Byron/gitoxide/commit/f565512c6d37c0532d0d138dd1db0456903a0d2a)) + - Refactor ([`9c4bc0a`](https://github.com/Byron/gitoxide/commit/9c4bc0a98bd024ca0a6e3d3f86f491dd92b880ac)) + - And it works! The new algorithm is sleeker, and really wants to be backported… ([`8e025b1`](https://github.com/Byron/gitoxide/commit/8e025b1177db12e0e4f2387e44e58815e703a054)) + - Thanks, clippy… ([`079ce9c`](https://github.com/Byron/gitoxide/commit/079ce9c07409ceb9acfc0eae900e73a4ae51fc58)) + - Basis for re-implementing core algorithm using new Tree data structure ([`be6caf4`](https://github.com/Byron/gitoxide/commit/be6caf4caf73fb61f23a4ea42617c3ca61b44569)) + - Refactor ([`290c29a`](https://github.com/Byron/gitoxide/commit/290c29ade648c7bb850c2e0629f8cc10967758fb)) + - Incorporate proper filtering of bases ([`0880998`](https://github.com/Byron/gitoxide/commit/08809986ac50081d91a9dbe8fd28c3452bf54e69)) + - Overhauled iterator logic, still missing 'is_root' filter ([`2bfbae1`](https://github.com/Byron/gitoxide/commit/2bfbae145e7d2256d41ed0a69e03d1e002534a49)) + - First impl of the Iterator shows it's 'unknown' what a root node is ([`3f32938`](https://github.com/Byron/gitoxide/commit/3f329380f6d13ab6ab991a5bb82e4cb38b37a52f)) + - Sketch on how children access could look like ([`16a35df`](https://github.com/Byron/gitoxide/commit/16a35dfcee905a672b2c1a0741320a51b3cf67d7)) + - How a referenced version would look like… ([`e36021d`](https://github.com/Byron/gitoxide/commit/e36021df6b6b872be1249dbddd96a2d678c3bcc3)) + - Refactor ([`62a01fe`](https://github.com/Byron/gitoxide/commit/62a01fee56b45ef83b4e3efb018af8ebb1db22ac)) + - More experimentation towards a safe tree data structure… ([`d907ce8`](https://github.com/Byron/gitoxide/commit/d907ce8f34ff488bc6a70f17d3c99df82b7ef41b)) + - First stab at new Tree datastructure… ([`85d7579`](https://github.com/Byron/gitoxide/commit/85d7579bf9c2f82f941b983ea4d54e16c6661c9b)) + - Safety for handling base pack offsets ([`17d8375`](https://github.com/Byron/gitoxide/commit/17d837514ad0f28771d67a64f74a30ef460fc3d1)) + - …but there seem to be issues with the kernel pack… ([`cc147bc`](https://github.com/Byron/gitoxide/commit/cc147bc60066c4ef31353a499958edadc960a9c4)) + - Quick and dirty impl of gitoxide layer for bundle writing, aka index-pack ([`e78386b`](https://github.com/Byron/gitoxide/commit/e78386b824010c5ca8efca87604c339d40b545ae)) + - Cargo clippy ([`586ba7a`](https://github.com/Byron/gitoxide/commit/586ba7af016f9a510b4ffeecc1aff6de0a569627)) + - Implement in-memory mode; refactor ([`0c195b9`](https://github.com/Byron/gitoxide/commit/0c195b92b59892c9f5369e28acd8f99d25f42c0c)) + - Refactor ([`c9d9298`](https://github.com/Byron/gitoxide/commit/c9d92980fd15e8e3568c82243d5eedb5e6e13f10)) + - Use monomorphic calls only at the expense of code siz ([`40b28d1`](https://github.com/Byron/gitoxide/commit/40b28d18736b09bf3af1a70e9854e98e94bd09fc)) + - Refactor ([`150d0bc`](https://github.com/Byron/gitoxide/commit/150d0bcc3ab39061be1add3f98da299e95edbbd5)) + - Also implement the 'no output directory' branch ([`5a3240f`](https://github.com/Byron/gitoxide/commit/5a3240fae2211924ac2eb03c9f57d2234de4f26f)) + - Refactor ([`68e52f8`](https://github.com/Byron/gitoxide/commit/68e52f8ce144f2daf2db407e66b3684a7d96d58d)) + - For the first time, writing an index could work with persistence ([`16e045c`](https://github.com/Byron/gitoxide/commit/16e045c3cd0f6e003a6b6e547360acdf99a06585)) + - Don't write pack to file if everything is kept in memory ([`f3ddda6`](https://github.com/Byron/gitoxide/commit/f3ddda6434824845e9abffab1d851c067428d8c7)) + - Allow data file to be optional in preparation for in-memory operation ([`95af105`](https://github.com/Byron/gitoxide/commit/95af105298e1073b71e3edcbbe3c9f3179ecf78e)) + - Refactor ([`413968d`](https://github.com/Byron/gitoxide/commit/413968dfee5e5a66ed9e63823f6bda5a5a22753e)) + - Refactor ([`5d27cdb`](https://github.com/Byron/gitoxide/commit/5d27cdb98b85baa5c544bc326ad50d1d7664116a)) + - Optional pack lookup depending on the settings ([`2b509de`](https://github.com/Byron/gitoxide/commit/2b509deefe7e09e59bd69937044337c8ac327f5f)) + - Write-through the pack file as we receive it and move it into place ([`6180e39`](https://github.com/Byron/gitoxide/commit/6180e3995426e99400364b04af36e0265ad779aa)) + - Receive progress information when reading packs in bundle ([`759091d`](https://github.com/Byron/gitoxide/commit/759091d3c6696b427d7b5aab1b6da05a0d268c04)) + - Start supporting writing packs to disk right away ([`f2203e0`](https://github.com/Byron/gitoxide/commit/f2203e0ebefaf254008f4ad4628218c42f1a2208)) + - Refactor ([`75c333c`](https://github.com/Byron/gitoxide/commit/75c333c121c402201ed4abf82ea7f14481d3f55b)) + - Prepare for implementing the bundle with various write modes ([`de420e4`](https://github.com/Byron/gitoxide/commit/de420e4515c6e4953a6e8cf6c632e3561873caca)) + - Bundle thread progress underneath reducer progress ([`76b1b2b`](https://github.com/Byron/gitoxide/commit/76b1b2b3015183129638b1f122a54fb8df8a1ac7)) + - Prevent deadlock, interestingly ([`ca02901`](https://github.com/Byron/gitoxide/commit/ca02901ad0eff63c3d9105a385c0ada6179ae71a)) + - Refactor ([`ea254c0`](https://github.com/Byron/gitoxide/commit/ea254c095465c880383d47a5284994a5a68a8769)) + - Rough progress for writing the index ([`f1a7f9b`](https://github.com/Byron/gitoxide/commit/f1a7f9b9ec71f2ae2de2c9bbe57f5118c76fa3dd)) + - Initial batch of progress usage for index creation… ([`b10e5c6`](https://github.com/Byron/gitoxide/commit/b10e5c664be9bd1bdb2b72b858ebaf35c1ed4cb4)) + - Refactor ([`77b3c21`](https://github.com/Byron/gitoxide/commit/77b3c213922c2f264722fc2423dbc22d0988c507)) + - Refactor ([`fb23d15`](https://github.com/Byron/gitoxide/commit/fb23d156c276484038761394a054a96d6f9ed087)) + - Refactor ([`7da7e08`](https://github.com/Byron/gitoxide/commit/7da7e080241c36b3a743e9bc01b61db5758246e5)) + - Refactor ([`5a3ad3a`](https://github.com/Byron/gitoxide/commit/5a3ad3a59da297c56ea47450b2c90dd24f542d40)) + - Refactor ([`785a23d`](https://github.com/Byron/gitoxide/commit/785a23d9b0ef3529ca4f655ed122a5e0c783b945)) + - Header encoding works now! As well as index writing :)! ([`024b854`](https://github.com/Byron/gitoxide/commit/024b854b07720f219fe12eefa94a166820523c9c)) + - Initial version of a complete header encoding impl, but… ([`ce6b46b`](https://github.com/Byron/gitoxide/commit/ce6b46b1bdcdf5ff5047d3288dc6fddb5bf62f77)) + - Looks like CRCs are not correct ([`3c4e4a0`](https://github.com/Byron/gitoxide/commit/3c4e4a0a61fe552913ec72c569d9a2095646b69a)) + - Cargo clippy ([`a5596fb`](https://github.com/Byron/gitoxide/commit/a5596fb71fd268b6faaa3b19c8b78d3608070012)) + - Fanout writing works now… ([`93a7ba9`](https://github.com/Byron/gitoxide/commit/93a7ba913fa29f734b98fe5723d01e2a7593ae2c)) + - It's a good idea to remove old code from time to time… ([`9e47f1b`](https://github.com/Byron/gitoxide/commit/9e47f1b04a5bbd4c0f13da5d55ae6302ae941d35)) + - Fanout table, but slowly I get it :D ([`cfd8a25`](https://github.com/Byron/gitoxide/commit/cfd8a25f9125c48afe4b66eab6b6ecf71097c486)) + - Fix decompression; fanout table is still wrong though ([`77fac1a`](https://github.com/Byron/gitoxide/commit/77fac1a01d8c15f9f772c3e14a430a890ff50899)) + - Despite writing the CRC32 now, it doesn't work yet ([`ecd12b9`](https://github.com/Byron/gitoxide/commit/ecd12b90aadd6bf6cdf551802918823670a45466)) + - First stab at streaming pack header encoding ([`3c6e78b`](https://github.com/Byron/gitoxide/commit/3c6e78bec9cbd4df842919cc8dc3c575414ed002)) + - Refactor ([`5925d46`](https://github.com/Byron/gitoxide/commit/5925d4615216dea70b5bc737b70f898e81e540e2)) + - Simplify offset handling in favor of allocating less ([`ce4ec62`](https://github.com/Byron/gitoxide/commit/ce4ec62e66a7fd9ff720633f531156ed51d610fe)) + - Only allocate memory for offsets if needed ([`72e0642`](https://github.com/Byron/gitoxide/commit/72e06421ae386dd15b34ce6dcf5e1cf666e70c3a)) + - First complete implementation of index writing… ([`826f996`](https://github.com/Byron/gitoxide/commit/826f996b9a9d877b84d286e18f5501eaec73d6f1)) + - Reduce contention by using the shared cache only once ([`c370e13`](https://github.com/Byron/gitoxide/commit/c370e133f4626a59eadbc8b70b4b5df39a34ad71)) + - Optimize CRC handling - no need to assign it after the fact ([`ffcc03d`](https://github.com/Byron/gitoxide/commit/ffcc03de5768c26c25dafbfbe523ca3bd4422336)) + - Assure we can deltas store theyr resolved buffer ([`d2a81d9`](https://github.com/Byron/gitoxide/commit/d2a81d912cdd9ec22ed8351b2a8395d85de46aa5)) + - And it does seem to work! Awesome! ([`71cd982`](https://github.com/Byron/gitoxide/commit/71cd9824bece6215745b02d9df001ae202fe2597)) + - Delta-application could work if we handle our buffer better ([`ac6100b`](https://github.com/Byron/gitoxide/commit/ac6100b094842f0a472be9789c024fc45939ff06)) + - Refactor ([`400a2a9`](https://github.com/Byron/gitoxide/commit/400a2a91edd72394de0aba55628154c16bca98bc)) + - One step before applying deltas ([`a074193`](https://github.com/Byron/gitoxide/commit/a07419303b3b9a24acb580a8653da952a5fa9964)) + - Prepare for delta application ([`9a9fb7a`](https://github.com/Byron/gitoxide/commit/9a9fb7a53fbda1b77d013f9806bd383a06135741)) + - Cargo clippy ([`d69c973`](https://github.com/Byron/gitoxide/commit/d69c973626fc554d34326b7ba37243b5389d2193)) + - Parse pack header before trying to decompress :D ([`9d1b44a`](https://github.com/Byron/gitoxide/commit/9d1b44ad98bb4cac55749ce25af5e444bc14d4ab)) + - Refactor ([`772e9ce`](https://github.com/Byron/gitoxide/commit/772e9cef82b1d58a1d7c9ad23dda570ec97bcc0b)) + - Consumer can resolve entries ([`13adce6`](https://github.com/Byron/gitoxide/commit/13adce6e18a4efb9da30dfc86c22a74dbc9026aa)) + - Refactor ([`c87f770`](https://github.com/Byron/gitoxide/commit/c87f77036eb8f8b095997afcf5200b165d9ddf2f)) + - Refactor ([`d9d406d`](https://github.com/Byron/gitoxide/commit/d9d406d77531bdfe5b33ee8ed17bccd431e85f9b)) + - First version of resolver to copy from a memory map ([`506b8fd`](https://github.com/Byron/gitoxide/commit/506b8fd94478ab259d18f4226c4b25bd080f775d)) + - Rethink resolver into something even simpler ([`4388c6c`](https://github.com/Byron/gitoxide/commit/4388c6c1ccbfbee7d5abb064eab3569a1aebf6a0)) + - Use parking_lot where possible ([`367874e`](https://github.com/Byron/gitoxide/commit/367874e91a2ca79d17c90d8ebaace1ee23efb4d9)) + - Consumers can fail gracefully ([`9082080`](https://github.com/Byron/gitoxide/commit/9082080a43e4db43378abc5555ad6f8084fdc111)) + - Refactor ([`1b4cad0`](https://github.com/Byron/gitoxide/commit/1b4cad01c6fb99e06db51415557c555ffb06b9f7)) + - Refactor ([`4ce13bb`](https://github.com/Byron/gitoxide/commit/4ce13bbe65403a2a9a320fb439ae797b19921862)) + - Support for decompression in case compressed bytes are stored ([`c1fcf28`](https://github.com/Byron/gitoxide/commit/c1fcf28f1069b605191652a2bd1556445e3b9833)) + - Computing hashes for bases from decompressed in-memory store works ([`7c19fe6`](https://github.com/Byron/gitoxide/commit/7c19fe6aec0cdb425d77cf13349e2f7f687c63e3)) + - Show that all data can be passed for processing in threads ([`a95ce9c`](https://github.com/Byron/gitoxide/commit/a95ce9c83920f29689ded1e374a224bef2d2b7cb)) + - A cache usable from threads ([`1d4879a`](https://github.com/Byron/gitoxide/commit/1d4879aee75a2c2ccbefdd48a2c2d339db38a23b)) + - Re-associate CRC32 with the correctly sorted ID output ([`037e1e5`](https://github.com/Byron/gitoxide/commit/037e1e5a92c430689674e2cb7e96f9738a92fde5)) + - Refactor ([`b3a365d`](https://github.com/Byron/gitoxide/commit/b3a365d179301f315d24884717c2dc09e34c3087)) + - Refactor ([`97eb524`](https://github.com/Byron/gitoxide/commit/97eb524ffa4cbf04113d3a622aca3a76606f0d96)) + - Use chunked input and calculate 'optimal' chunk and thread sizes ([`0cc74d7`](https://github.com/Byron/gitoxide/commit/0cc74d7982577866c6fa6d7b0f56073979142bf0)) + - Generalize chunk iterator ([`905e85e`](https://github.com/Byron/gitoxide/commit/905e85e0910650b139a845c7e7bae97a7ae5b215)) + - First rough cut of in_parallel invocation ([`8f16081`](https://github.com/Byron/gitoxide/commit/8f160810f6baf0fca5590001dd89895fccae0bbe)) + - Prepare for parallelization ([`cb36596`](https://github.com/Byron/gitoxide/commit/cb36596d3059700deaf87a26df344f5dbb87f1f4)) + - Simplify indexing step ([`070899c`](https://github.com/Byron/gitoxide/commit/070899cd8cb86ac3761255ccba72225ffd6c518e)) + - Resolver look ups may now turn out empty… ([`a991923`](https://github.com/Byron/gitoxide/commit/a9919230896b9129fe91b5e12dc6e0f03547b5e9)) + - Allow us to stop searching for bases early when resolving ([`e7874da`](https://github.com/Byron/gitoxide/commit/e7874dad3982829d82d3e708926e2965eca3ef4e)) + - This should be the interface for building indices from packs directly ([`f5295d0`](https://github.com/Byron/gitoxide/commit/f5295d09592753089569543b843a352fd91df201)) + - Got a good idea on how this will work! ([`7bb229f`](https://github.com/Byron/gitoxide/commit/7bb229fbb17fb8cc8251c49b681511519a9a6b9c)) + - Keep track of the pack trailer ([`cdba61e`](https://github.com/Byron/gitoxide/commit/cdba61ea90a5e4f4e64ca2fe7777da540dbbf09c)) + - Now I understand why there is a separate resolution phase… ([`1c2bcbd`](https://github.com/Byron/gitoxide/commit/1c2bcbd3f510dddfc43e3f02a7987890306d8db7)) + - Fix tests ([`b9866b6`](https://github.com/Byron/gitoxide/commit/b9866b683687e210279b88b5409f01d52659f550)) + - Prepare a way to gradually implement V2 index writing ([`92a4986`](https://github.com/Byron/gitoxide/commit/92a4986fcec21870abfbe8a7886fa428d5d47941)) + - Refactor ([`feba75b`](https://github.com/Byron/gitoxide/commit/feba75b9f04459abc341bd2482a393a69602b054)) + - We can now restore (possibly half-written) packs ([`b1daa46`](https://github.com/Byron/gitoxide/commit/b1daa465c40ea8c7c9de69a18e467d69459d911e)) + - Prepare ability to restore pack files ([`76583e5`](https://github.com/Byron/gitoxide/commit/76583e58ad8a4a4269fb857364b213ae12d4ea9b)) + - Support for pack trailer verification when iterating ([`f37f131`](https://github.com/Byron/gitoxide/commit/f37f131cf6904780147371746ff5bf56dbc21356)) + - Also read the pack trailer during iteration ([`98a8e17`](https://github.com/Byron/gitoxide/commit/98a8e17e791b6bcd92149d7ff68cbc9d9ceee087)) + - Only take as many objects as we are allowed (without 'take(…)') ([`86f5853`](https://github.com/Byron/gitoxide/commit/86f585344f968ba86a19b58129fe3bd2a058730c)) + - Refactor ([`e15bde4`](https://github.com/Byron/gitoxide/commit/e15bde409cd1eae30a3a4b45624f52025144a10a)) + - Shift thin pack resolution to another work bucket; test for index writing ([`2592361`](https://github.com/Byron/gitoxide/commit/25923611663a244908198c4dc656ac73cc16c841)) + - Refactor; better tests ([`12d14bf`](https://github.com/Byron/gitoxide/commit/12d14bfe2aa089723a395287c5100aad6e838935)) + - Refactor ([`bd66a85`](https://github.com/Byron/gitoxide/commit/bd66a8592d3d2f5c6a7393c261f19023d14d2f37)) + - Now keeping track of read bytes works ([`d32d921`](https://github.com/Byron/gitoxide/commit/d32d9210133ab339cece3b8811958eadb8428587)) + - An attempt to intercept bytes read from bufread - FAIL ([`8db04f6`](https://github.com/Byron/gitoxide/commit/8db04f66fe4a4c5d0dba1c2a0c82723b4487f5bf)) + - Refactor ([`2d817d7`](https://github.com/Byron/gitoxide/commit/2d817d7b3fcb939067b7b94fa7aeac20382effc8)) + - Refactor ([`893f65b`](https://github.com/Byron/gitoxide/commit/893f65b63b424922b8cdc496a9e798acc498c1c6)) + - Refactor ([`12816bc`](https://github.com/Byron/gitoxide/commit/12816bc715a0d0bad338a00c394c4cc503b20c3e)) + - Refactor ([`56f763a`](https://github.com/Byron/gitoxide/commit/56f763a44538e053b4f674543720720fcc1af5d4)) + - Associate HashKind with the kind of pack ([`d66d139`](https://github.com/Byron/gitoxide/commit/d66d1391a3edee0572e07cb421527a57d90de9d9)) + - Move all pack-related file handling to bundle; big refactor ([`f8b6e75`](https://github.com/Byron/gitoxide/commit/f8b6e7524b6d73406dc6ff7b8e9c7e22322efd78)) + - First step towards putting the index file into position ([`d994c74`](https://github.com/Byron/gitoxide/commit/d994c74d7cd9c9c004bf27f0b2ac23558ce9c50d)) + - Initial interface trial for writing pack index files ([`936bdcc`](https://github.com/Byron/gitoxide/commit/936bdcc29e5531026c1b0e83d9084501fc6ded9c)) + - Refactor; more thorough tests ([`82d87ce`](https://github.com/Byron/gitoxide/commit/82d87ce35b1e68a07057807d28afffa7acc03b7f)) + - Cargo clippy ([`b768b56`](https://github.com/Byron/gitoxide/commit/b768b56db4274b7cc313e8a6c09f3c46a48a2829)) + - At least make it configurable if to keep decompressed bytes or not ([`28ebcae`](https://github.com/Byron/gitoxide/commit/28ebcae69e95c768e4d9567ec6cc8adacd8d520b)) + - And streaming iteration works, even though we are forced to allocate… ([`27d624d`](https://github.com/Byron/gitoxide/commit/27d624d920a0ea92cf506363a505517676ced770)) + - Yes, this really cannot work: StreamingIterator ([`b4df430`](https://github.com/Byron/gitoxide/commit/b4df430b96561c63d20bb5de442582eca79768f1)) + - In the moment we tried to actually return Entry<'a>, it didn't let me :D ([`8367955`](https://github.com/Byron/gitoxide/commit/836795514f19a9d43039be228c5183061db4a404)) + - First steps towards making the InflateReader reusable ([`83a97d4`](https://github.com/Byron/gitoxide/commit/83a97d462e16d6e28151c2bf6eb7b201f4982dce)) + - Better error handling in iterator, fuse yourself ([`5ebacc4`](https://github.com/Byron/gitoxide/commit/5ebacc491a5148d31bf5ebe2746ea3d5c562b407)) + - The next() impl shows that we should be less lenient ([`4521cab`](https://github.com/Byron/gitoxide/commit/4521cab497757c34501b8eefd3b2d7d36b4df32b)) + - Provide entries which borrow from iterator ([`86eea13`](https://github.com/Byron/gitoxide/commit/86eea1326a48cf55c8a17505d2cf7c44a110a878)) + - Provide a lifetime for iterator (and possibly its entries) ([`7852bd1`](https://github.com/Byron/gitoxide/commit/7852bd193ad5659f07fc8759ca3597b037ad0255)) + - First version of expected iterated data types ([`d5e7d31`](https://github.com/Byron/gitoxide/commit/d5e7d311f38ffff0a31f85feaab692f078a75bb5)) + - Improved iterator constructors ([`fb71f04`](https://github.com/Byron/gitoxide/commit/fb71f0463519c886d2e5ab30a32d546e70fb0606)) + - Better handling of pack headers ([`0030bdb`](https://github.com/Byron/gitoxide/commit/0030bdbe3d476f6dac9c98f273d72666e2a9b7eb)) + - Frame for a pack iterator ([`07d1096`](https://github.com/Byron/gitoxide/commit/07d109652a6ccb93d166296cd1f91babbd1ae0aa)) + - Some more tests ([`9095728`](https://github.com/Byron/gitoxide/commit/9095728dff0f5ae221dcf3345e81cfb54300e03d)) + - Verification for pack objects ([`17bd95e`](https://github.com/Byron/gitoxide/commit/17bd95ec43ca2814165823026fd85a776208fe21)) + - Refactor ([`3ee947e`](https://github.com/Byron/gitoxide/commit/3ee947e241404cdac3225824b1434c2b270236da)) + - 'stream()' now assures all data is decompressed ([`32e994c`](https://github.com/Byron/gitoxide/commit/32e994c60f58f1be839c6dc07d819ac31f30af1d)) + - It looks like something is wrong with the object stream implementation ([`d187b5a`](https://github.com/Byron/gitoxide/commit/d187b5a769b62ec706c1265e0db8403327d8e92d)) + - Loose object verifycation - but it doesn't seem to work as expected ([`9dd5676`](https://github.com/Byron/gitoxide/commit/9dd56761ae75eac691449cd86a1be04c11c0fecb)) + - Refactor ([`37cfd9b`](https://github.com/Byron/gitoxide/commit/37cfd9ba14726d6fd38b5ba6eabb3b17be263779)) + - Refactor ([`8e3b9fc`](https://github.com/Byron/gitoxide/commit/8e3b9fc23a139c8307e052afa8d1d6f6f562ca1d)) + - Prepare full 'verify' implementation ([`ee45c7f`](https://github.com/Byron/gitoxide/commit/ee45c7f47b95fc406cc5922a322c8fd6c0f52775)) + - Refactor ([`0a33b24`](https://github.com/Byron/gitoxide/commit/0a33b24f5b61ccdf1358f1e9adcf0f6fd4099c1c)) + - Always compress values when using a sink when exploding packs ([`70562fa`](https://github.com/Byron/gitoxide/commit/70562fa123faf51bd72a4aedb12acb0d3247e4e2)) + - Support for compression even when using sink ([`105c845`](https://github.com/Byron/gitoxide/commit/105c84551361bd93ec549a07ab377a7f1ae97332)) + - Another stab at fixing stress tests :) ([`7db6a33`](https://github.com/Byron/gitoxide/commit/7db6a33bc8bdaccf9091acc2ca48eb26f8a8c1fa)) + - Fix stress test; improve progress messages ([`37ccd92`](https://github.com/Byron/gitoxide/commit/37ccd92bbc4eb9917c1916e39f626ecddbf85064)) + - Ignore decode errors (if configured) at the right spot ([`e53141d`](https://github.com/Byron/gitoxide/commit/e53141dd5e319d29de15ab73a783ce21158ed54a)) + - Tests for relaxed error handling ([`93c0e26`](https://github.com/Byron/gitoxide/commit/93c0e2664ccc259747543845186c4211ae139008)) + - Nice error message on failure ([`adbc82c`](https://github.com/Byron/gitoxide/commit/adbc82c31450681fcb38233eeb8095efc5e52a18)) + - Inform about deleted files using progress ([`a3ee516`](https://github.com/Byron/gitoxide/commit/a3ee5160093c9326006fcedbf1f507d8978a97c2)) + - Fix error display - certainly something to watch out for ([`38eff2c`](https://github.com/Byron/gitoxide/commit/38eff2c3f0bb6170a253b4c96f01077c1358bc40)) + - The first 'explode' implementation… ([`0d31ad1`](https://github.com/Byron/gitoxide/commit/0d31ad1b61997fa0d0692c5919fb8032ffaaa35b)) + - Support for skipping various safety checks during traversal ([`0416666`](https://github.com/Byron/gitoxide/commit/0416666d3492ddd031188f750371248f5f67d598)) + - Prepare for configuration of safety checks ([`06638d0`](https://github.com/Byron/gitoxide/commit/06638d0f9ce50782e2897d76c742c526758889d1)) + - Cargo clippy ([`95e02c9`](https://github.com/Byron/gitoxide/commit/95e02c951ace19f6ace49a9190607674d98c970d)) + - Restore original verification functionality ([`0e3c1b9`](https://github.com/Byron/gitoxide/commit/0e3c1b9bb9841ae4bb0ef1df2e72e950f7a7fd33)) + - Nearly there! Interesting that anyhow errors must be sync! ([`eaee77e`](https://github.com/Byron/gitoxide/commit/eaee77ea4ce10f5c85b42a33452eef996adac3bf)) + - Finally it compiles with returning Boxed errors! Ouch… ([`1fc8252`](https://github.com/Byron/gitoxide/commit/1fc8252a24b75faa88065838a3e9ffa13e6f7f54)) + - First sketch of new verify expressed in terms of traversal ([`4cb570f`](https://github.com/Byron/gitoxide/commit/4cb570f96ddd7ee2faa62e54927afd78ba7822af)) + - Refactor ([`f2832a8`](https://github.com/Byron/gitoxide/commit/f2832a840d0bc69e7ee0817e3617ac0b3d40e4fd)) + - Finally a progress can be passed to the delegate… ([`a9f4de0`](https://github.com/Byron/gitoxide/commit/a9f4de0783a87b0693f87da98283e30ec72f3737)) + - Refactor ([`bbb3e1e`](https://github.com/Byron/gitoxide/commit/bbb3e1efd309bbcdb3adda84308a3fc644389e43)) + - Pass all arguments (but progress) to processor ([`1e87922`](https://github.com/Byron/gitoxide/commit/1e87922299762dc0b2cf0800e1ff1e0a61467ce5)) + - Call a bare version of the traversal processor ([`95a5cea`](https://github.com/Byron/gitoxide/commit/95a5cead30fa4e7904b28158a747ac28adadf01e)) + - Preparation for abstracting the 'process object (stateful)' function ([`fe400f5`](https://github.com/Byron/gitoxide/commit/fe400f572accb396def704f7853d5e81a42839de)) + - Discard idea of making traversal even more generic ([`1525f36`](https://github.com/Byron/gitoxide/commit/1525f36d29574699d2fcb16b70678121030fd109)) + - Initial step towards separating verification from traversal ([`d14b4fc`](https://github.com/Byron/gitoxide/commit/d14b4fc7fd09bf1a96b16d583c1a8df102517650)) + - Refactor ([`bae7781`](https://github.com/Byron/gitoxide/commit/bae7781ab549f0daa73980a29d18d64320601470)) + - Rename verify-pack to pack-verify (keeping it more formal) ([`ec8c48a`](https://github.com/Byron/gitoxide/commit/ec8c48a8fcbcd748c9c764734d881b5f0217e1e4)) + - Refactor ([`f580441`](https://github.com/Byron/gitoxide/commit/f5804410eb80fa406294fb83e161b09a4f3bf1a2)) + - Fast implementation for buffered input ([`c50b150`](https://github.com/Byron/gitoxide/commit/c50b150adc7c5379017237c0914c294aad1fdc7c)) + - Respect object size to be 64 bit where applicable… ([`61c8aba`](https://github.com/Byron/gitoxide/commit/61c8aba769a52d11de549505f6b4cbca1d949758)) + - Better errors for writing disk objects ([`f7bc137`](https://github.com/Byron/gitoxide/commit/f7bc1372d6b445f5c078632c4f3ad7786f98e6a9)) + - Try to use HashKind where possible ([`b32e01d`](https://github.com/Byron/gitoxide/commit/b32e01dbfd257d123a461380df5dcfcb88c77e1e)) + - Refactor ([`a3777ed`](https://github.com/Byron/gitoxide/commit/a3777edb2612b50de7a12da4ecbf707638d23ac3)) + - Clippy happy ([`a938c70`](https://github.com/Byron/gitoxide/commit/a938c7002b4c4905694d97dc682dd77cd6780cff)) + - And writing of loose objects works ([`bbfe7bf`](https://github.com/Byron/gitoxide/commit/bbfe7bf2be3ab04dd27b9a23381ced9838fc292e)) + - This seems to be a working deflate write implementation ([`0acce38`](https://github.com/Byron/gitoxide/commit/0acce381912059c06df55955e45245c5eeb6d4b3)) + - The first succesful inflate run for small input ([`94e1c5a`](https://github.com/Byron/gitoxide/commit/94e1c5a69d22ee56d697e6e59a6a367ceb5e0c6f)) + - What seems to be a reasonable write implementation for deflate ([`45a28d2`](https://github.com/Byron/gitoxide/commit/45a28d259c2f08b8c3c96b0bf0092261d0bd17a3)) + - Another test to understand the deflate streamer better ([`4256038`](https://github.com/Byron/gitoxide/commit/4256038e65bc68222c60c1b25273a3d066991970)) + - Refactor ([`dd463df`](https://github.com/Byron/gitoxide/commit/dd463df3b4a48d117596d78e050bf425db350b27)) + - Refactor ([`0b42237`](https://github.com/Byron/gitoxide/commit/0b42237ead90a1582b5ddb936d30fbf75da8b6b1)) + - Refactor ([`5b0bb84`](https://github.com/Byron/gitoxide/commit/5b0bb841bbc5f2e267238e4cdec69029a8344a31)) + - Put down a few tests to understand how deflate wants to be fed ([`178a018`](https://github.com/Byron/gitoxide/commit/178a01814b344c7b2ae7a7470c33808dca7e3a38)) + - Refactor ([`0d8d7fe`](https://github.com/Byron/gitoxide/commit/0d8d7fee0fde7f24c91fb147745ed8474f40e834)) + - Improve looks of documentation ([`11a32eb`](https://github.com/Byron/gitoxide/commit/11a32ebc2209d1a05eb4c4ec5131e85dfb43d9f6)) + - Fix tests for now… ([`79ab945`](https://github.com/Byron/gitoxide/commit/79ab9453264562488e8c5bc6ead7dd1c1fe46cba)) + - Refactor ([`0cd7bb7`](https://github.com/Byron/gitoxide/commit/0cd7bb74e379483116afb1ab618081ef1bfef67a)) + - Complete and unoptimized disk writer for objects, but… ([`9d0c3f1`](https://github.com/Byron/gitoxide/commit/9d0c3f16413d437fa893524c1cdf4a899fc3c921)) + - Refactor ([`62e75bc`](https://github.com/Byron/gitoxide/commit/62e75bca7de17782dc5b7cbae29c8ce8e63b8d02)) + - Make use of HashKind in Write trait ([`0304dd0`](https://github.com/Byron/gitoxide/commit/0304dd0f44cd55af07796c3aacca0f116ffd181b)) + - Make our Sink API similar to std::io::sink() ([`a03ae0f`](https://github.com/Byron/gitoxide/commit/a03ae0f064cbf63bc4cb352ccec25333ec1843e6)) + - Finish Sink implementation ([`84f7908`](https://github.com/Byron/gitoxide/commit/84f7908b1883ed6c484ca4e522ac530c8cc161d5)) + - First steps towards serialization tests for sink ([`e8d52c6`](https://github.com/Byron/gitoxide/commit/e8d52c6997997688220959b096d46aaa641d14a1)) + - Introduce hash kind, as this should be specified when writing an object ([`f5d0acf`](https://github.com/Byron/gitoxide/commit/f5d0acf61ac5dd815bc5ece4462eb9a43dd9c44a)) + - A simple trait for writing owned objects and streams ([`68b7d7d`](https://github.com/Byron/gitoxide/commit/68b7d7defdb07b3a100bc16a9167ee957647f5cb)) + - (cargo-release) version 0.2.0 ([`76fe0ab`](https://github.com/Byron/gitoxide/commit/76fe0ab5f0b58504a5ea5adb74b349b9d588e51e)) + - (cargo-release) version 0.2.0 ([`0bb8314`](https://github.com/Byron/gitoxide/commit/0bb831480d8657e1bb29ee7009aeac673471403e)) + - Use 'optimized' chunk size for 'less-time' algorithm ([`c8c23c0`](https://github.com/Byron/gitoxide/commit/c8c23c0fb9ab0174dd33299ddd3f257f7b2dde78)) + - Incorporate dynamic chunking into 'less-time' algorithm ([`295aa2f`](https://github.com/Byron/gitoxide/commit/295aa2f01dc596a8880cd2f68a8d83bc6913ce48)) + - Integrate new chunk size code into lookup code ([`a8422cf`](https://github.com/Byron/gitoxide/commit/a8422cf0b0c9ff4d3275cc17a68a74811b5bd01f)) + - Simplify progress code using `inc()` ([`9e8df59`](https://github.com/Byron/gitoxide/commit/9e8df59d9a6349c49dd80447cbdbde95090e1f04)) + - Add 'inc()' convenience methods to progress ([`2e46c9b`](https://github.com/Byron/gitoxide/commit/2e46c9b72a2a5b90bcdac249de07ffbc124cfb04)) + - Run clippy first; pacify clippy ([`0a5b883`](https://github.com/Byron/gitoxide/commit/0a5b883c22f2df8a6d51f75c5e09bdfdf276fee4)) + - Use faster algorithm by default ([`bb45c3d`](https://github.com/Byron/gitoxide/commit/bb45c3d8a2aabf87231981000240f0444abf6fc4)) + - Properly compute delta chain length by default ([`a93b894`](https://github.com/Byron/gitoxide/commit/a93b89464e4484bc7100d5934f14a7321f3ca7a4)) + - Remove hits_to_live ([`3a3fae9`](https://github.com/Byron/gitoxide/commit/3a3fae9a8f637481d526d28a695c3f411c1a89a8)) + - Attempt to auto-remove unusable deltas… ([`5dd8243`](https://github.com/Byron/gitoxide/commit/5dd8243ceafbb2a89964708f5f9b2783953677aa)) + - Now with cache (and due to that, incorrect statistics for now) ([`efd28d2`](https://github.com/Byron/gitoxide/commit/efd28d21acd97709f68ff9404131123cda527cbd)) + - Make chunk statistics independent of traversal method ([`6225f36`](https://github.com/Byron/gitoxide/commit/6225f36cc4735dd41b0c01d7c7ce6ed61f384e9a)) + - First working version of alternate object traversal, without cache ([`51b5eb6`](https://github.com/Byron/gitoxide/commit/51b5eb6c3a91e323c92e3e8f4069a12cda904354)) + - Initial state for indexed lookup ([`acbcd79`](https://github.com/Byron/gitoxide/commit/acbcd79942e9783ca60ac41010a73ef98031d3e9)) + - Refactor; tests now fail with more than just not-implemented ([`310a2f7`](https://github.com/Byron/gitoxide/commit/310a2f7f5498ed48777eec53b830b9f7dece33c3)) + - Speedup entry sorting a little; use less memory ([`b4df372`](https://github.com/Byron/gitoxide/commit/b4df37258734e55d4679870c639f993305ada73c)) + - Better index entries sorting progress ([`b4d7038`](https://github.com/Byron/gitoxide/commit/b4d7038ae729c2631277b0d5ca842a20c609abe9)) + - Prepare sharing even more code ([`61c76cf`](https://github.com/Byron/gitoxide/commit/61c76cf6f856f79fd2c77e8ed9cf8940b29d6a50)) + - Make use of shared reducer in upcoming indexed verify implementation ([`290eae1`](https://github.com/Byron/gitoxide/commit/290eae115a1df277c4331bb7f2994265da117656)) + - Use shared reduce implementation in lookup based algorithm ([`10fc88d`](https://github.com/Byron/gitoxide/commit/10fc88d492821cf67de4cea9beefef4b77d4452b)) + - Prepare for integration of general reducer ([`c37832e`](https://github.com/Byron/gitoxide/commit/c37832eb8a6b08cf965c287a104bfdead02776d2)) + - Refactor; enable testing of reverse-delta lookup ([`512daf9`](https://github.com/Byron/gitoxide/commit/512daf94038f675353271c930694e0577ac746b4)) + - Revert "Move deallocation off into own thread" - not worth it! ([`051da15`](https://github.com/Byron/gitoxide/commit/051da1572a8ed8a99108a337f802ae5f7cc9491e)) + - Move deallocation off into own thread ([`90230f1`](https://github.com/Byron/gitoxide/commit/90230f1c0cdd1c9091a3f5e6d9393e05b6c0abb5)) + - Implement more cache-friendly pack offset v2 retrieval ([`00cf84b`](https://github.com/Byron/gitoxide/commit/00cf84baeee9932196288c8641f18621610d47a9)) + - Refactor ([`3c25c67`](https://github.com/Byron/gitoxide/commit/3c25c6778b3d4fbba9906e0f5b37acbce6c69c61)) + - Initial refactor of DeltaTree, but… ([`6384649`](https://github.com/Byron/gitoxide/commit/63846499367a3f106cf668cb84606ca355ad7a3d)) + - Measuring performance of sorting index offsets is quite revealing ([`4b16336`](https://github.com/Byron/gitoxide/commit/4b163366cbf5b8e314e1913e24a1d19179e25611)) + - Properly handle the BufReader to make indexing work; FAST ([`57e95cf`](https://github.com/Byron/gitoxide/commit/57e95cf79c78285283be88ca9e7baf56c1ad58c0)) + - Avoid seek in favor of skimming a file read in bursts ([`01ae405`](https://github.com/Byron/gitoxide/commit/01ae4053ee57f35875d843f00d390acc19e56849)) + - Some performance information in progress ([`20aef2c`](https://github.com/Byron/gitoxide/commit/20aef2cf0e0212d5d79a6a4b7ece328adffbdf23)) + - Nodes now provide access to the pack offset ([`61c1497`](https://github.com/Byron/gitoxide/commit/61c1497547ee2789f5a90735b72b06186030c3d3)) + - Basic tree access for the entry graph ([`c5e5c77`](https://github.com/Byron/gitoxide/commit/c5e5c77aea3981d4f3b0ad528ae25eccdc58ae85)) + - Fix clippy ([`ec40e09`](https://github.com/Byron/gitoxide/commit/ec40e093d72f93d86168f39ebaca5b122ca0bec3)) + - Hookup new indexing step ([`313064f`](https://github.com/Byron/gitoxide/commit/313064f1875fea6165f9d7feeb31ce0183959044)) + - Frame for running the new streaming code on bigger packs ([`e0b34eb`](https://github.com/Byron/gitoxide/commit/e0b34eb87bbf29b31c87d298cdb68e6e0fa5349b)) + - Refactor ([`fdfab40`](https://github.com/Byron/gitoxide/commit/fdfab408c38087c5afcdd028e988089c56311baf)) + - Refactor ([`1fbeb35`](https://github.com/Byron/gitoxide/commit/1fbeb35cb1a0e66d7e12d678f351fecedc7978dd)) + - Refactor ([`385e935`](https://github.com/Byron/gitoxide/commit/385e9356f49fb9e1e87f13137ee270b34527fc0b)) + - Now it works :D ([`008b4de`](https://github.com/Byron/gitoxide/commit/008b4defcfbccdd61bc7f5f2c9a8e939f817095d)) + - Initial (failing) implementation of building an index tree ([`25dc83d`](https://github.com/Byron/gitoxide/commit/25dc83d660c832cc68306395c7bd303ae806ac07)) + - Easy access to sorted offsets in pack index files ([`d93540f`](https://github.com/Byron/gitoxide/commit/d93540fe2a6d4bb70248e82d039d6a2665354ef3)) + - Refactor ([`cb8d561`](https://github.com/Byron/gitoxide/commit/cb8d56101bdc4cd7e3fa95ac79f82c1cda99871c)) + - Refactor ([`c7ae705`](https://github.com/Byron/gitoxide/commit/c7ae7056eb6a33656b0db31bf1c1012b7ffa2ca8)) + - Refactor ([`2fc449c`](https://github.com/Byron/gitoxide/commit/2fc449cdefab1d10a446f83bd2462d1034808d97)) + - Change course and do pack streaming first ([`bcb275e`](https://github.com/Byron/gitoxide/commit/bcb275e91cfd6f0a71b3cb59a2b706b60608a594)) + - Roundtrip Rust repo in stress test; accept more diverse trees when parsing ([`0347cdb`](https://github.com/Byron/gitoxide/commit/0347cdbf473d80c016745ffbaf582832fe2eba2a)) + - Allow some very special trees not to be round-trippable ([`8fe1358`](https://github.com/Byron/gitoxide/commit/8fe1358aa9375bbe63f1ee64174b9e663d140a05)) + - Consume PGP signature in tags fully ([`ffd6c31`](https://github.com/Byron/gitoxide/commit/ffd6c31aa3adecc2dea6357373d88a495d63ba0d)) + - Make tagger signature optional ([`3358f9a`](https://github.com/Byron/gitoxide/commit/3358f9ae539c7b7878d87a209d678d2f08f94b1b)) + - Remove now unused pgp_signature field - it's in extra-headers ([`c8c937c`](https://github.com/Byron/gitoxide/commit/c8c937c505e455572544a1a9da1b991ef4662b97)) + - Proper support for extra-headers ([`d0feb2b`](https://github.com/Byron/gitoxide/commit/d0feb2b5b30f9719bf3b40ac5b74f8a5a8515bc9)) + - Switch to latest quick-error ([`9760856`](https://github.com/Byron/gitoxide/commit/976085614ee13a19fc1347209259a3dcf36ef95b)) + - Fully implement --encode and --re-encode flags ([`a7cfac8`](https://github.com/Byron/gitoxide/commit/a7cfac83ddd859d9c2c25e457c0d7043738792dc)) + - Refactor ([`56b66ac`](https://github.com/Byron/gitoxide/commit/56b66ac069f24635a8fa74b4b2231dfb0a82a1ef)) + - Prepare for re-encoding each pack object ([`afae684`](https://github.com/Byron/gitoxide/commit/afae684c72e5dc4b718976056dd5d34ed61de72a)) + - Fix build with rustc 1.45 ([`8c2a1ee`](https://github.com/Byron/gitoxide/commit/8c2a1ee853c5354117fc0a1b6719108785633915)) + - Refactor ([`ec5e50f`](https://github.com/Byron/gitoxide/commit/ec5e50f607d59302d6db3944f6ea7b667f820927)) + - Prepare for writing out owned trees ([`2b6eced`](https://github.com/Byron/gitoxide/commit/2b6eced325057a884d56ed9db755a8699cbf8d00)) + - Use borrowed::Id in trees for full type safety ([`5d57c1f`](https://github.com/Byron/gitoxide/commit/5d57c1f7e3b9a84f7b46a4378015572155f3104b)) + - Refactor ([`f7b8826`](https://github.com/Byron/gitoxide/commit/f7b8826ba144f54f3a3fe6096a5daafd29e25002)) + - Fix odb test ([`a792f44`](https://github.com/Byron/gitoxide/commit/a792f44fec60d63aaa16538bf06fb29277e78433)) + - Prepare for allowing an owned, processed version of multi-line headers ([`f966e7f`](https://github.com/Byron/gitoxide/commit/f966e7f26cbbe99e5508215adaacf073e108bf48)) + - Use borrowed::Id everywhere ([`9f876f0`](https://github.com/Byron/gitoxide/commit/9f876f04feaa3fd3bba9729fff7667708dc0c4be)) + - Move git_object::Id into git_object::owned::Id - much better already! ([`50c7136`](https://github.com/Byron/gitoxide/commit/50c71368a69f57b0a43061df105685e992ed384a)) + - Basic integration of borrowed Id; translate between owned and borrowed ([`84ff638`](https://github.com/Byron/gitoxide/commit/84ff638a183567593ace8056de2a856304d29d1d)) + - Prepare to allow Id be owned and borrwed; abstract over hash type ([`d883c31`](https://github.com/Byron/gitoxide/commit/d883c31dd14f253a3af153616007c9231fdf265a)) + - Introduce the notion of IdRef ([`7007361`](https://github.com/Byron/gitoxide/commit/700736197b903cb6fe9ed60718e49e4be44199a7)) + - Use statically known borrowed arrays for perfect type safety! ([`3ead048`](https://github.com/Byron/gitoxide/commit/3ead048bb999e6266831df2ca6c2022013529ab2)) + - Refactor ([`766f3e4`](https://github.com/Byron/gitoxide/commit/766f3e491dc6ebcca20753cda3487545268721eb)) + - Refactor ([`bca1f16`](https://github.com/Byron/gitoxide/commit/bca1f16a6f3da497e3488e333d5ebc99e39ee689)) + - 'data -> 'a as it's shorter and also more idiomatic ([`71821e9`](https://github.com/Byron/gitoxide/commit/71821e938887f448f1458642eda2bac365f2aa85)) + - Refactor ([`dedd4dc`](https://github.com/Byron/gitoxide/commit/dedd4dc91c26dfef368307345bb9e8d49637207c)) + - Refactor ([`de0bc3c`](https://github.com/Byron/gitoxide/commit/de0bc3cb4a32bf4cd02ce7c8420bc008e469b779)) + - Refactor ([`e5391d3`](https://github.com/Byron/gitoxide/commit/e5391d36d192c3c12426102f734d2e227c568a08)) + - Refactor ([`163909b`](https://github.com/Byron/gitoxide/commit/163909b593e21860d0a292c6e45daee93fb270fb)) + - Refactor ([`49f64db`](https://github.com/Byron/gitoxide/commit/49f64db88fc0643e0bff215efdb9b1b429b648ba)) + - Refactor ([`9f825b8`](https://github.com/Byron/gitoxide/commit/9f825b849d14494a2d58a09eb6499fd86fd05af3)) + - Refactor ([`2fbc2e1`](https://github.com/Byron/gitoxide/commit/2fbc2e1f76f972758b0c880d3eabdf75586749e7)) + - Fix naming change, which was introduced accidentally ([`fbb9f98`](https://github.com/Byron/gitoxide/commit/fbb9f98508ec722e192466e28ded47aef2fb78b3)) + - Make it easier to validate bundles, for completeness ([`8ea05de`](https://github.com/Byron/gitoxide/commit/8ea05de8d1ae49e09465a66354cf69dd7c7a2e05)) + - Refactor ([`34e85f2`](https://github.com/Byron/gitoxide/commit/34e85f2242b12ec1560b8e50bc9ab447cd1805fc)) + - Refactor ([`b3bde87`](https://github.com/Byron/gitoxide/commit/b3bde870054cb4001c1e2ea8a81b1a4b1d83405b)) + - Refactor ([`0b540c2`](https://github.com/Byron/gitoxide/commit/0b540c236b3459b340f13908ca52c82e40378e13)) + - Refactor ([`2888f1b`](https://github.com/Byron/gitoxide/commit/2888f1b10a2baf40155544e667ddd461f3ddc938)) + - Refactor ([`0817b24`](https://github.com/Byron/gitoxide/commit/0817b24fae6106db2d9e3fcfcdcb10b9a182911d)) + - Refactor ([`dcacd3b`](https://github.com/Byron/gitoxide/commit/dcacd3b06d7a4532c600dfdf62e03561e8ed55ef)) + - Refactor ([`b113da9`](https://github.com/Byron/gitoxide/commit/b113da945715f9611eb0fb79925d1239eaf1569c)) + - Refactor ([`6659174`](https://github.com/Byron/gitoxide/commit/66591745f08d15f3756a352f4041c807ea92fc6f)) + - Refactor ([`bed5dc8`](https://github.com/Byron/gitoxide/commit/bed5dc80c5b307c6d35f7b4405693dce1f7f6d71)) + - Refactor ([`4867740`](https://github.com/Byron/gitoxide/commit/486774096a86f6eb001d812e6ac9ab0b29791148)) + - Refactor ([`f6cc80e`](https://github.com/Byron/gitoxide/commit/f6cc80e5f0ae966c83345be64219fa7ebe0e1db2)) + - Refactor ([`8b416d4`](https://github.com/Byron/gitoxide/commit/8b416d4b8417c04ea5d3527a88190d867dc8b7c2)) + - Refactor ([`23e05d7`](https://github.com/Byron/gitoxide/commit/23e05d78c73b2bfce3025b3e34746d48026b34ed)) + - Refactor ([`d3b36f4`](https://github.com/Byron/gitoxide/commit/d3b36f4ad8a5cb9266542ee997941c879121be96)) + - More tests for various object types ([`f4703e0`](https://github.com/Byron/gitoxide/commit/f4703e047834d13748f21db861fd0a753d5b1233)) + - Refactor ([`86fa00f`](https://github.com/Byron/gitoxide/commit/86fa00f0967dba5453f7226125123ef398e48790)) + - Basic decode implementation ([`7ff02cb`](https://github.com/Byron/gitoxide/commit/7ff02cb84469f5aa4a3be1489927344b45385a45)) + - Support for in-pack object lookup in Bundle::locate ([`7e3d6be`](https://github.com/Byron/gitoxide/commit/7e3d6be5136d9c3816bedd3b9797186457aeb476)) + - First dummy implementation of borrowing a buffer provided by the user ([`9c31fcb`](https://github.com/Byron/gitoxide/commit/9c31fcb7c25be5c75e3dad1e940683b8ae42b935)) + - Make it easy to learn that objects couldn't be located by using options ([`a916f36`](https://github.com/Byron/gitoxide/commit/a916f367d329927369b127a5f2fba63e8d4d9d88)) + - Mild refactor - need combined pack + index ([`6bf8ed4`](https://github.com/Byron/gitoxide/commit/6bf8ed470803ab58737b119d892b7eabb77fd8b9)) + - Respect thread limit in 'in_parallel' ([`babfd84`](https://github.com/Byron/gitoxide/commit/babfd84cba77ef7a0f541ba921b31ebd3f3c50e3)) + - Apply cargo diet ([`79b9b73`](https://github.com/Byron/gitoxide/commit/79b9b7398be608de1f439f56b057dc08d421081f)) + - Add missing license description ([`2b80181`](https://github.com/Byron/gitoxide/commit/2b80181ad428a9bf267a9660886f347a850fc76f)) + - Make crates publishable ([`5688a34`](https://github.com/Byron/gitoxide/commit/5688a3427ff3673e1422d43106f4d685fa837aed)) + - Cargo clippy (from CI) ([`0a28857`](https://github.com/Byron/gitoxide/commit/0a288579545345c0dffdfa814b052959baec0a34)) + - Proper implementation of line renderer into 'lean' CLI ([`e98e7c2`](https://github.com/Byron/gitoxide/commit/e98e7c280d73e9d9ebd13202afb93a56cb2f7c9c)) + - Handle windows newlines in test suite for packs as well. ([`ebd5176`](https://github.com/Byron/gitoxide/commit/ebd517633f099582dc2633e71f7bb7890acd14d1)) + - Add metadata to allow docs.rs build all featueres ([`10f9386`](https://github.com/Byron/gitoxide/commit/10f9386a12decc1f13999aee72be484c8f6d48ce)) + - Update tasks ([`269280a`](https://github.com/Byron/gitoxide/commit/269280a0e00c9eee54d591b96c3ed0e9d4202489)) + - Allow to limit the logging depth for less cluttered output ([`fce7035`](https://github.com/Byron/gitoxide/commit/fce703531d7006f7d961d6ffa66f51f6c9bc0efc)) + - Looks like this performs much better already, but… ideally subprogress isn't shown ([`3b96d18`](https://github.com/Byron/gitoxide/commit/3b96d18483a845f7692f94cc40c28871fd96e479)) + - Finally speed up logging progress properly - needs input throttling ([`1a550c6`](https://github.com/Byron/gitoxide/commit/1a550c6458b10fad2e42b641899216c5517c6e26)) + - Provide average throughput per second ([`5b23d17`](https://github.com/Byron/gitoxide/commit/5b23d171102ad859258b9673bf35561ef9e8f246)) + - Git-odb with serde support ([`0da930c`](https://github.com/Byron/gitoxide/commit/0da930cf23f215cc1e2bda8f7340a5d69370735a)) + - Remove dependency to git-object from git-features - it better remains free ([`67c3a6a`](https://github.com/Byron/gitoxide/commit/67c3a6ab4cc32358a1406c2f863e26a4c2929867)) + - Commit to using bstr whenever something is not data bytes; remove miniserde ([`3183d1b`](https://github.com/Byron/gitoxide/commit/3183d1b02c2d7bb3c750f8472c29bb161641ca7f)) + - Prepare centralization of bstr as optional component ([`aa857d9`](https://github.com/Byron/gitoxide/commit/aa857d9df32dfc75f151154ca430ddfee907deed)) + - \#[forbid(unsafe)] for all crates ([`afda803`](https://github.com/Byron/gitoxide/commit/afda8039259b7a30cfed5dbcdd9caf4773b4c234)) + - Allow for more screen space when formatting ([`6794300`](https://github.com/Byron/gitoxide/commit/67943002e7f4215b5383bd0538786ce2857f011e)) + - Prepare next task ([`74bcbb5`](https://github.com/Byron/gitoxide/commit/74bcbb506585aa9e0955253d07bab111f83f014e)) + - Display object throughput per second, even though it won't be visible in TUI… ([`53b4513`](https://github.com/Byron/gitoxide/commit/53b4513f6a8bb2f2e5b07fa72a3085e620cee24c)) + - Disable LRU cache if we have to get statistics ([`befba3b`](https://github.com/Byron/gitoxide/commit/befba3b769195fb592d714afe12194a61ae4a330)) + - Wonderful statistics on compression efficiency! ([`1bb09c5`](https://github.com/Byron/gitoxide/commit/1bb09c509dae4e493ab05022bbf51c0b1786d479)) + - Count objects per chain level ([`209d53f`](https://github.com/Byron/gitoxide/commit/209d53f531ec9bcffbb04ba060447bee59ad26f6)) + - Pass average stats through to the top level ([`5b4979c`](https://github.com/Byron/gitoxide/commit/5b4979c1dfeb9a29974dd4e6529ae5da074d0b1a)) + - Refactor ([`4dd9fd4`](https://github.com/Byron/gitoxide/commit/4dd9fd4a2c48380bda9a865ef704e7fdfa7e5b89)) + - Closer to actually producing statistics ([`5f087ec`](https://github.com/Byron/gitoxide/commit/5f087ec30a50775ad8bb67f21e352fe9ee1ccc9f)) + - Refactor ([`7add82c`](https://github.com/Byron/gitoxide/commit/7add82c39169e3c2fff76c48cdd318fe6040d7bc)) + - Also average statistics on chunk level ([`3b927e5`](https://github.com/Byron/gitoxide/commit/3b927e50173e3feae72cde8a226cee524275403a)) + - Provide more detailed information when decoding an entry ([`80c5da9`](https://github.com/Byron/gitoxide/commit/80c5da9bd88e1f329292f3f93ba53c8ff8324a20)) + - No need to say 'begin' before doing something, it's primarily for logging ([`13eba3a`](https://github.com/Byron/gitoxide/commit/13eba3a3484068939436996352fe5585aa221bca)) + - Throughput for pack ([`81f5c33`](https://github.com/Byron/gitoxide/commit/81f5c335b224dd85062c9208cee2bb288ad3e833)) + - Print performance stats at the end of hashing the index ([`9c94417`](https://github.com/Byron/gitoxide/commit/9c9441709c9a759a3a0916402921c7beeb735d75)) + - Assure hashing progress is dropped when done ([`db6e067`](https://github.com/Byron/gitoxide/commit/db6e067c5dd90311d174881546d8df8f521eb552)) + - First implementation of logging per thread ([`477dd90`](https://github.com/Byron/gitoxide/commit/477dd90ce5e102875b19489bf8ae9877522ef9c8)) + - Support for providing progress to threads ([`2815858`](https://github.com/Byron/gitoxide/commit/2815858adf7ac0f7b4cbc88cf05df0ea6aef4116)) + - Properly count objects ([`d398e7e`](https://github.com/Byron/gitoxide/commit/d398e7e68ad893d21a088ec6ac727dc8577317fc)) + - First very basic progress implementation ([`b820717`](https://github.com/Byron/gitoxide/commit/b8207177daee8a9ffa23c7c052cf9ca651b15804)) + - Pass progress everywhere, for now just to discard it ([`da3ae1c`](https://github.com/Byron/gitoxide/commit/da3ae1c82cd726b8fae9b8d26069719930e9ba99)) + - Control which hashing crates to use from the top-level as well. ([`dfe9b20`](https://github.com/Byron/gitoxide/commit/dfe9b203b2e877a7e345b4f2942bf5a1582ab43e)) + - Use git-features to toggle 'parallel' mode from the 'gitoxide' level ([`d944fbf`](https://github.com/Byron/gitoxide/commit/d944fbf181acc5fb83a841613174702af1e074d6)) + - Sketch out `Progress` trait; don't forget to write docs at some point ([`534b3c7`](https://github.com/Byron/gitoxide/commit/534b3c73101fd1b885de630523ab706bd06a327b)) + - Refactor ([`baeb4ef`](https://github.com/Byron/gitoxide/commit/baeb4ef1a9680c212ce9d1010e2c34eedafcd246)) + - Refactor ([`e12bfd6`](https://github.com/Byron/gitoxide/commit/e12bfd645bb2f707a1b5077190d9f37393a8e315)) + - Make `in_parallel` trait bound more loose: Clone instead of copy ([`3e91b05`](https://github.com/Byron/gitoxide/commit/3e91b0512919c02899324564b8f571ce534955d9)) + - Using all cores actually does speed things up ([`ed944b9`](https://github.com/Byron/gitoxide/commit/ed944b9480ae647c7e75a7a07a9c59885725b3a0)) + - Also run index+pack validation in parallel; only parallelize bigger packs ([`dc15b26`](https://github.com/Byron/gitoxide/commit/dc15b2652cc6b9e94f80aebfeec8f879ae5a529f)) + - Avoid running anything in parallel for small packs ([`c2df183`](https://github.com/Byron/gitoxide/commit/c2df183943e8b533c4cd5f5833f61ad94942943d)) + - Don't send every single entry, instead send reasonably sized chunks ([`56298a6`](https://github.com/Byron/gitoxide/commit/56298a62ea8cc9c6fef7f682ffb8ddda5404ca9b)) + - Refactor (down to 6 minutes for big pack verification) ([`4157b51`](https://github.com/Byron/gitoxide/commit/4157b5196936e9f5f884a645f7e1c37ba6b13b52)) + - First working version of actually parallel `in_parallel` ([`145ee39`](https://github.com/Byron/gitoxide/commit/145ee399e2c057aec3330e26bafb7910ca7dc56d)) + - First implementation of 'parallel' without threads. How will scoped fare? ([`735744e`](https://github.com/Byron/gitoxide/commit/735744e1960a3055b836767c85613ba9d147cdd4)) + - A sketch of a minimal helper for parallel work ([`377252a`](https://github.com/Byron/gitoxide/commit/377252a3b4869952059e832ce32656e2cf2a674c)) + - Refactor ([`be4795f`](https://github.com/Byron/gitoxide/commit/be4795f00d7b693cb52f93857ac3b4b65340053f)) + - Refactor ([`3e2efff`](https://github.com/Byron/gitoxide/commit/3e2efffc945a0737c2d8b820a93b013e6ffa45e2)) + - Bigger LRU caches are better, but with this one we can't go too large ([`5e1f7ae`](https://github.com/Byron/gitoxide/commit/5e1f7aedc970552d3ec4ab3358757af790ce6628)) + - First implementation of an LRU cache - it gets hit, let's see how it fares! ([`5a21031`](https://github.com/Byron/gitoxide/commit/5a21031415a6e2ca43cb828492fd5517d2a98e9e)) + - Also set the cache with bases and deltas ([`915a3fb`](https://github.com/Byron/gitoxide/commit/915a3fb21c950dd35a97a735375a144bbc59e3b1)) + - First sketch of cache implementation - get() is there, next is put() ([`ce54756`](https://github.com/Byron/gitoxide/commit/ce547565de23e89212bf6197178191ddf5b11fd3)) + - Allow delta base resolution to fail (similar to how lookups can fail) ([`b721424`](https://github.com/Byron/gitoxide/commit/b7214241dfbb85c3115e230fa502f790133e2192)) + - Allow in-pack lookups for V1 packs ([`2e51bbb`](https://github.com/Byron/gitoxide/commit/2e51bbbab4a47001ef725d0bf8bf5714d0c37e70)) + - Add CRC32 reading at index ([`268f855`](https://github.com/Byron/gitoxide/commit/268f855da9db5f694bedb073493778147d646271)) + - Pack offset by index ([`69e35b1`](https://github.com/Byron/gitoxide/commit/69e35b1d8f24f366d675484a1bddbebd37b72e22)) + - V2 pack lookup ([`9e56902`](https://github.com/Byron/gitoxide/commit/9e56902bdb7702181809c6a4c2280750ddd64044)) + - Test V1 lookup ([`e9c7127`](https://github.com/Byron/gitoxide/commit/e9c71271fa51d5420fcb205d2d3deb6d012f0d41)) + - Add CRC32 check during pack verification ([`04ff1a0`](https://github.com/Byron/gitoxide/commit/04ff1a0bf9aa164e9cff262ec521eab76c2e4688)) + - Prepare for CRC32 check - needs understanding of size of bytes in packed object ([`3ab2df1`](https://github.com/Byron/gitoxide/commit/3ab2df1e00eb41e8a222b208131f63ba3e065df5)) + - Refactor ([`dd2d623`](https://github.com/Byron/gitoxide/commit/dd2d6238771ff86df6a412a6d817aa92a5e5ed43)) + - Finally delta-objects can be read as expected. ([`81f2f54`](https://github.com/Byron/gitoxide/commit/81f2f547bad33c414f6e12d16df4922274b06758)) + - Definitely an improvement to the way add-deltas are applied… ([`c6cdb12`](https://github.com/Byron/gitoxide/commit/c6cdb12b47f6d5f4e3f02895acb2de08a7df00cc)) + - Fix one issue with Trees being declared as tags ([`ada66cd`](https://github.com/Byron/gitoxide/commit/ada66cdbab0fec4765428ce815c0868d34d5babf)) + - Validate sha1 of pack objects, some work, some don't for some reason… ([`aa8799a`](https://github.com/Byron/gitoxide/commit/aa8799a01b92c3c3b7d4347f745921bbb685c454)) + - Capability to write loose object headers, fast ([`de0aeff`](https://github.com/Byron/gitoxide/commit/de0aeff518ebd218b73bf472558f278f6bcdc17c)) + - Refactor ([`5364bbe`](https://github.com/Byron/gitoxide/commit/5364bbe0415c37f684066e22eb017fe5d7ca7c64)) + - Fix another implicit assumption that doesn't hold: deltas are NOT… ([`093637d`](https://github.com/Byron/gitoxide/commit/093637da964b807fa767009732e9b93002e35fab)) + - Finish delta-application to take into account the biggest possible result… ([`0ee2b69`](https://github.com/Byron/gitoxide/commit/0ee2b696014012864b0645bd1b9da508cb1e465c)) + - First stab at dealing with bigger-than-expected intermediate result sizes… ([`8027ff4`](https://github.com/Byron/gitoxide/commit/8027ff4de7ffe6126cf1ade4938baa08899cb938)) + - First simple implementation of fetching all objects in a pack (without validation) ([`053045b`](https://github.com/Byron/gitoxide/commit/053045bb23e2a85e2a1d16eeb65c399dfabba5b4)) + - Support for verifying pack files and index files ([`b09b4e1`](https://github.com/Byron/gitoxide/commit/b09b4e1f35c3802dfd3418bda42b96828acd9ec8)) + - Simple index file verification (internal) ([`1d27050`](https://github.com/Byron/gitoxide/commit/1d27050f21ee1c8f492d38e14c294fa31a7b48a1)) + - Refactor ([`4023b02`](https://github.com/Byron/gitoxide/commit/4023b0260b0b139853f8dc1b9260045a8dac6e47)) + - Refactor ([`855a769`](https://github.com/Byron/gitoxide/commit/855a769026f81739f28b38507c0bef7b59e97a8b)) + - Refact[r ([`c84410b`](https://github.com/Byron/gitoxide/commit/c84410b2b0e66c10c30fc70c3674c971b270204d)) + - Refactor ([`c24c79d`](https://github.com/Byron/gitoxide/commit/c24c79d65b947625a5a9ab73dbd3afdef060fa12)) + - Test --no-default-features for git-odb ([`2394bca`](https://github.com/Byron/gitoxide/commit/2394bca4a76247c420fe06c59d0d76819c6e978b)) + - Refactor; prevent trailing bytes to become part of the digets ([`043813c`](https://github.com/Byron/gitoxide/commit/043813cd2e49b358e17ad78d975ef255924c78fa)) + - Try a version that doesn't rely on memory mapped files for throughput… ([`d59ddfc`](https://github.com/Byron/gitoxide/commit/d59ddfcf50edd0bfc8252e6c7a68c86fe27b5a9f)) + - Try to speed it up with prefetching - not really :D ([`8485185`](https://github.com/Byron/gitoxide/commit/8485185bcb7895461dc4347f25b9f0b0bab54594)) + - Simplify folder names ([`36fde1f`](https://github.com/Byron/gitoxide/commit/36fde1f90e9034060b5ede8a923365474659085e)) + - Fix LSB parsing code with python based code written 6 years ago :D ([`c12fdad`](https://github.com/Byron/gitoxide/commit/c12fdadbf839ce6f8a638fe25667d870a8f6b808)) + - Improved packed header parsing… it works a little better now it seems, but… ([`ca779ed`](https://github.com/Byron/gitoxide/commit/ca779edc457f1f1baed05e8c64bb2994f6b12945)) + - Refactor; and figured out what the header parsing issue is ([`d364049`](https://github.com/Byron/gitoxide/commit/d3640493e509b782589b4c0680962e6e1f2ae665)) + - Some more tests ([`85e541f`](https://github.com/Byron/gitoxide/commit/85e541f36fd7795c53d0dc3d07d5b76a6725c889)) + - Refactor; better error handling ([`031df11`](https://github.com/Byron/gitoxide/commit/031df11a3c3767330c9f13cab0e55c2559a72e9b)) + - First very rough version of full-object decompression without allocation ([`7c704a7`](https://github.com/Byron/gitoxide/commit/7c704a71e51607149a7a6a1293a401f4c7ecb610)) + - Refactor ([`dcb1997`](https://github.com/Byron/gitoxide/commit/dcb19971841d3330df63c67f73793f0a45b6c74f)) + - Refactor ([`baaf06e`](https://github.com/Byron/gitoxide/commit/baaf06e36605f9b79ef09dd7cbdbb42fb16b64be)) + - Refactor ([`3edaaec`](https://github.com/Byron/gitoxide/commit/3edaaec2fad6594049a0f10a4bf921dc3c485ac0)) + - Finish Object Reader implementation, now for in-memory objects, too ([`35e69b8`](https://github.com/Byron/gitoxide/commit/35e69b87521eef89705012a7170517670ee20e7c)) + - A simpler implementation to skip the header ([`47ca6ab`](https://github.com/Byron/gitoxide/commit/47ca6ab2ff0cbf8801d0a82cebbbeb8c4f62cdae)) + - Allow skipping the header when decompressing files (streaming) ([`ff35032`](https://github.com/Byron/gitoxide/commit/ff350323e4a424df8c17a9dca53cc8967e45e960)) + - First step towards supporting skipping the header in the stream ([`8e45f53`](https://github.com/Byron/gitoxide/commit/8e45f5370516b0df9df4e984d29161d399697fdd)) + - Fix stream decoding - it seems to work, but we need to deal with the header ([`f10ed75`](https://github.com/Byron/gitoxide/commit/f10ed75a74c183edeb2a5bd665e5649a5b282e93)) + - Tests for streamed reading of bigger objects (FAIL) ([`b4a6b72`](https://github.com/Byron/gitoxide/commit/b4a6b7233ff4f4154d1dd46a29a88787746899f8)) + - Refactor ([`80aad4b`](https://github.com/Byron/gitoxide/commit/80aad4b97b76b26050c87eac483b8af1fcfb61ed)) + - Add missing parts to implement Read, need refactoring to make it work though ([`13d4cdb`](https://github.com/Byron/gitoxide/commit/13d4cdb32fe197d1517270183d9547ddf1aa381e)) + - First step towards streaming of ZLIB deflated content ([`a870f7a`](https://github.com/Byron/gitoxide/commit/a870f7a5bca9f57374e7c9582866473fbbce6e5e)) + - Cleanup ([`a2f0a5d`](https://github.com/Byron/gitoxide/commit/a2f0a5dec0b183712e03397e8b4340fed77ce008)) + - Fix clippy ([`a9c5da7`](https://github.com/Byron/gitoxide/commit/a9c5da7132eeaa6806b8190985a7aa25f9ef89d8)) + - Make decompression of bigger objects work (on the fly) ([`7e4f5a9`](https://github.com/Byron/gitoxide/commit/7e4f5a9594b31c67a49a1d2d6a063241ab8821d9)) + - It becomes obvious that this way of decompressing things won't work ([`1818bda`](https://github.com/Byron/gitoxide/commit/1818bda0acc83354b093c39831e2844d48eb5637)) + - Don't do so much logic if we already decompressed everything ([`26cb36c`](https://github.com/Byron/gitoxide/commit/26cb36ce3717a63ca7934e7bbc35052208227056)) + - Refactor ([`423b885`](https://github.com/Byron/gitoxide/commit/423b8857f1dc580d64ec4075f955d34524979269)) + - More convenient access to our four object types ([`ecda6d2`](https://github.com/Byron/gitoxide/commit/ecda6d23561dc176f7d7ad2565da8105efac614f)) + - It's proably OK to make parsed pack entries avaialble, why not ([`8a64e10`](https://github.com/Byron/gitoxide/commit/8a64e10ae5206e10f487fbde88412037c165e583)) + - Refactor ([`13f0e77`](https://github.com/Byron/gitoxide/commit/13f0e77c0d67f8078bfaf96c3bb735f8c3161a3f)) + - Memory size checks for objects ([`ab51616`](https://github.com/Byron/gitoxide/commit/ab51616bb250a62b5367e861c25c1d90ec60f720)) + - Reduce loose Object memory footprint ([`38a81b0`](https://github.com/Byron/gitoxide/commit/38a81b0fc3ef1bff54779f0cf531ea2e0f82ebd8)) + - First Blob test for blobs that are already in memory ([`f503324`](https://github.com/Byron/gitoxide/commit/f503324b33fd7289782fe642b1f566e9d101ceab)) + - Make single-field objects blob and tree more explicit ([`1aef68f`](https://github.com/Byron/gitoxide/commit/1aef68f7e979324eb94966d44c160ffe537ee4a8)) + - Add Blob type to parsed objects ([`d3e8e4b`](https://github.com/Byron/gitoxide/commit/d3e8e4b24ecda84665b994ccad768774efdcdc90)) + - See 'parsed' blobs as in-memory representations… ([`6a6e105`](https://github.com/Byron/gitoxide/commit/6a6e105b3e438380b55f9e9566f0acd76c5efffd)) + - Make clear that not all objects can be parsed at the expense of convenience ([`ce3031d`](https://github.com/Byron/gitoxide/commit/ce3031da8ba1eb3e66d72474a8efc65c2990bc99)) + - Don't conflate errors with 'there is no suitable object' to parse ([`b9b796f`](https://github.com/Byron/gitoxide/commit/b9b796f69ced726167d72615e5628263a3158a35)) + - Fix imports ([`10f2967`](https://github.com/Byron/gitoxide/commit/10f29675442c76b38e0a8deb757930a13af3a3bb)) + - Try pub use with rename. Not bad in the docs, but maybe a bit confusing ([`526f3f8`](https://github.com/Byron/gitoxide/commit/526f3f8d3ca9fe9672b0518f1bc3b921f695c0d8)) + - Refactor ([`b9a1647`](https://github.com/Byron/gitoxide/commit/b9a16473ed028abc59fc5126db9530f2107498d8)) + - Integrate Commit object into Loose DB ([`7e9fe50`](https://github.com/Byron/gitoxide/commit/7e9fe505f08def0378c967514a9389da9e46301d)) + - Test for parsing trees from loose dbs ([`4f48249`](https://github.com/Byron/gitoxide/commit/4f4824971d62d165fd4c2bea869fd88986dc259f)) + - Refactor ([`9f9ccad`](https://github.com/Byron/gitoxide/commit/9f9ccad37fea96954a2df9e314b6c154466dc0ca)) + - Refactor ([`427c480`](https://github.com/Byron/gitoxide/commit/427c48007016e95b13d8750df8b6ac1620f465ac)) + - Refactor loose db ([`6ea4f53`](https://github.com/Byron/gitoxide/commit/6ea4f5331f8d4279025e3f912315af50f0eedbdc)) + - Handle commits without newlines; make tag newlines optional ([`c0b54be`](https://github.com/Byron/gitoxide/commit/c0b54bef5a2bcfce9b6deb90cdd27c7e0cc85810)) + - Make Commit available in borrowed object ([`b2d1b5d`](https://github.com/Byron/gitoxide/commit/b2d1b5d684bdfda5f922b466cc13d4ce2d635cf8)) + - Avoid unnecessary allocation when creating SHA1 paths in loose ODB ([`09d8d3a`](https://github.com/Byron/gitoxide/commit/09d8d3a12e161a7f6afb522dbe8900a9c09bce06)) + - First silly attempt to randomly remove an allocation ([`4ff2168`](https://github.com/Byron/gitoxide/commit/4ff21686c32a6edc84ea041c3040f33ae24f9519)) + - Document existing use of unsafe, deny everywhere else ([`41f4bce`](https://github.com/Byron/gitoxide/commit/41f4bce9d9a492f8e20a6eb5b3eaf5adc6d78329)) + - Cleanup integer parsing in loose object database ([`ecdce1a`](https://github.com/Byron/gitoxide/commit/ecdce1a05d8c732afd53c6da6067bf591f96fa6a)) + - The defining property is actually that the object is borrowing data ([`e0125fd`](https://github.com/Byron/gitoxide/commit/e0125fdb0a41ed139364084f6d679932f08b7b4f)) + - Fix cargo fmt ([`642dd13`](https://github.com/Byron/gitoxide/commit/642dd13afa77ea9c0f4a20d59f54b84bf6ca3333)) + - Cleanup; all tests work! ([`7c96603`](https://github.com/Byron/gitoxide/commit/7c9660354484869681356a8c4ef8057313e864f2)) + - First version of tag message parsing - it's actually changed now ([`74b2328`](https://github.com/Byron/gitoxide/commit/74b2328fcbbcffab9981c23e903c4f4c5d085aff)) + - Remove itertools in favor of vendoring the little code we need ([`8340508`](https://github.com/Byron/gitoxide/commit/834050878b43bae677287767332adc746a8aa2ed)) + - Optimize macro usage ([`0c9960b`](https://github.com/Byron/gitoxide/commit/0c9960b1a9404ec8db62ffeeedb3e482eba81c77)) + - Optimize dependencies ([`3ea2195`](https://github.com/Byron/gitoxide/commit/3ea2195090728f17ae425e4816405f10b7eb8a14)) + - Use git-object in git-odb ([`07f7c31`](https://github.com/Byron/gitoxide/commit/07f7c318d55603e3731f08cb04d3da8ac2fcfea6)) + - Add the latest nom, hoping it will be come out of alpha… ([`85958f1`](https://github.com/Byron/gitoxide/commit/85958f1771b521f905528ca426404b846244e122)) + - Refactor; use pretty-assertions for massively more readable test-failures ([`ea8d311`](https://github.com/Byron/gitoxide/commit/ea8d3113c32fff85c02d8ff2217adc6b42153137)) + - Switch everything parsed to BStr ([`62ae90a`](https://github.com/Byron/gitoxide/commit/62ae90a37d0dea33a23eb7d026cdf9b719692078)) + - Refactor ([`9a86f63`](https://github.com/Byron/gitoxide/commit/9a86f6352ccd5178198ad87df44d88358b475d1a)) + - Use btoi to parse all integers, directly from ascii-bytes ([`4f6ef42`](https://github.com/Byron/gitoxide/commit/4f6ef42a0b871096f81bd0cb9759aa651a1943d0)) + - Refactor ([`2990902`](https://github.com/Byron/gitoxide/commit/299090296fb3a2074c74289c4645b79d3f736ed0)) + - Move parsing tests close to actual parsing ([`3ca2c59`](https://github.com/Byron/gitoxide/commit/3ca2c592d91c9aa8fab8ed749871d6d96f2ef4e2)) + - Move examples into demos, having their very own dependencies; optimize tests ([`b757712`](https://github.com/Byron/gitoxide/commit/b757712f82de1d75ed813e744f979c1c652350e6)) + - Fix (untested) extraction of delta object information ([`55a56b7`](https://github.com/Byron/gitoxide/commit/55a56b70b7b5a80089fde2edfff3ab3743d61cdd)) + - Parallelize git-conut, optimize for speed ([`debd044`](https://github.com/Byron/gitoxide/commit/debd0445ba482d7b4424e53c45c0b6acf8b1de37)) + - Refactor ([`9fc9fc0`](https://github.com/Byron/gitoxide/commit/9fc9fc0e706eaced2a4f04ae082f9f5acdde1fc0)) + - Fix big-pack 64 bit offset handling in index v2 ([`3b485b5`](https://github.com/Byron/gitoxide/commit/3b485b57062765b7ea476feaed328f4f94fc3478)) + - Make refactor ([`cd6a18a`](https://github.com/Byron/gitoxide/commit/cd6a18ace5c07b542475518f6cfb506d34547013)) + - Cargo clippy first pass ([`8b0a2a8`](https://github.com/Byron/gitoxide/commit/8b0a2a8b0665cb4bd7c32e46bec9dc33114e4985)) + - Finally remove failure and equip example with anyhow ([`f5e4ec5`](https://github.com/Byron/gitoxide/commit/f5e4ec5804efec4966ab1ca7fbf6e1a757f2f8c2)) + - Remove failure from Index ([`55034a7`](https://github.com/Byron/gitoxide/commit/55034a7a22404b2d6a117c7242852480f42b84ab)) + - And one more module without failure ([`d0575bf`](https://github.com/Byron/gitoxide/commit/d0575bf39e6eebd0337bb8712eda1141b5766e92)) + - A big step towards removing failure ([`d862bd8`](https://github.com/Byron/gitoxide/commit/d862bd87a4d5bcafce83eed6c49c15a093972416)) + - Refactor ([`87c8a2e`](https://github.com/Byron/gitoxide/commit/87c8a2e288140b04e163fe85266d040d039ec69c)) + - Get rid of failure crate in favor of quick-error ([`91c8fc1`](https://github.com/Byron/gitoxide/commit/91c8fc1f0c50af55d7cb233bbe813c6d12fe11bc)) + - Get rid of nightly requirement, just parse tags differently soon ([`f037c4d`](https://github.com/Byron/gitoxide/commit/f037c4d982f2158cf173dce898c8dda1aea14106)) + - Cargo fmt ([`2aa0857`](https://github.com/Byron/gitoxide/commit/2aa085752aa3e99b51034a3dec882aea8c27ad94)) + - Reorganize repository a bit; use different contact email address ([`cb9fa28`](https://github.com/Byron/gitoxide/commit/cb9fa2848476e30767deb9d9807c649e0bc366da)) +</details> + +## 0.40.1 (2023-01-10) + +A maintenance release without user-facing changes. + +## 0.40.0 (2023-01-09) + +A maintenance release without user-facing changes. + +## 0.39.0 (2022-12-30) + +A maintenance release without user-facing changes. + +## 0.38.1 (2022-12-26) + +<csr-id-46636e64c9a48ec0e85e014ac0cc8b48846d8462/> + +### Bug Fixes + + - <csr-id-4fffa9a9198cf3012fa8215796aab3d456519ff3/> remove panic-assertions in `loose` `lookup_prefix` + +### Refactor + + - <csr-id-46636e64c9a48ec0e85e014ac0cc8b48846d8462/> flatten errors into one + By adding one variant, one can remove the previous 'sub-error', for which + there is no precedent in the codebase yet. + +## 0.38.0 (2022-12-19) + +### New Features + + - <csr-id-e9d1f45e944e91bb9715a3ee89a4f28b09250411/> support for pack-order when iterating objects. + - <csr-id-7f19bd7e63d78e3151e43d5094ae9d35cbe34f46/> add `loose::Store::try_header()` to obtain loose object information without content. + - <csr-id-c8835c6edae784c9ffcb69a674c0a6545dbb2af3/> upgrade to `prodash 21.1` and add `Ids` to all progress instances. + That way callers can identify progress they are interested in, say, for + selective visualizations. + +### New Features (BREAKING) + + - <csr-id-d9d05b0db6b4453e7385117d466bf7c2e8de81fa/> add `Store::try_header()` for obtaining object information quickly. + Note that this feature also comes with various refactorings related to the error + type used by various methods in order to get away from a 'one error fits all' kind + of situation. + +## 0.37.0 (2022-11-21) + +### Bug Fixes + + - <csr-id-1ce3190000f6211ce31468c7603d491bb5b90293/> Disable tag.gpgSign in test scripts + This is done for the same reason that commit.gpgsign is disabled for test + scripts. It prevents test failures if the user has tag.gpgsign enabled in + their global git config when invoking tests. + +### New Features (BREAKING) + + - <csr-id-3d8fa8fef9800b1576beab8a5bc39b821157a5ed/> upgrade edition to 2021 in most crates. + MSRV for this is 1.56, and we are now at 1.60 so should be compatible. + This isn't more than a patch release as it should break nobody + who is adhering to the MSRV, but let's be careful and mark it + breaking. + + Note that `gix-features` and `gix-pack` are still on edition 2018 + as they make use of a workaround to support (safe) mutable access + to non-overlapping entries in a slice which doesn't work anymore + in edition 2021. + +### Bug Fixes (BREAKING) + + - <csr-id-1fabdc51b9468ba2c6b8cf74509ad5aa2a0b86f4/> `alternate::resolve(…)` now takes the current_dir as argument. + That way it's more consistent with similar low-level functions and it's + possible to avoid multiple calls to `std::env::current_dir()`. + + Furthermore, the usage of `current_dir()` is made explicit when + instantiating a store to allow it to be reused. + +## 0.36.0 (2022-11-08) + +A maintenance release without user-facing changes. + +## 0.35.0 (2022-11-06) + +A maintenance release without user-facing changes. + +## 0.34.0 (2022-10-10) + +Maintenance release without user-facing changes. + +## 0.33.0 (2022-09-20) + +Maintenance release without observable changes. + +## 0.32.0 (2022-08-28) + +Maintenance release without user-facing changes. + +## 0.31.2 (2022-08-24) + +<csr-id-f7f136dbe4f86e7dee1d54835c420ec07c96cd78/> + +### Chore + + - <csr-id-f7f136dbe4f86e7dee1d54835c420ec07c96cd78/> uniformize deny attributes + +### New Features + + - <csr-id-b1c40b0364ef092cd52d03b34f491b254816b18d/> use docsrs feature in code to show what is feature-gated automatically on docs.rs + - <csr-id-517677147f1c17304c62cf97a1dd09f232ebf5db/> pass --cfg docsrs when compiling for https://docs.rs + +## 0.31.1 (2022-08-17) + +### New Features + + - <csr-id-81e1a9d38aac9e6dd0618266ff826593e038cce8/> Add `Cache::has_object_cache()` and `Cache::has_pack_cache()` methods. + That way it's possible to conditionally set or change the cache size. + +### Bug Fixes + + - <csr-id-41d494365d281056c5e9466860db808bd85143e9/> improve error messages when objects aren't found + - <csr-id-87f974eea2cf7c6e3405b2816d3ef2bd058fc3dc/> incorrect desired object kind if retrieved object doesn't have the + +## 0.31.0 (2022-07-22) + +This is a maintenance release with no functional changes. + +### New Features (BREAKING) + + - <csr-id-95210cb2ba85f75148b4ef48ccea9d9f8a0a0114/> Provide optional `candidates` for ambiguous entries during `lookup_prefix()` + The candidate entries are all entries matching a given prefix. + - <csr-id-92d8be1101a7e76e70cd90db6a943b9e31e20802/> `loose::Db` and `Store` can return all candidate objects for a single prefix + This is the first step towards auto-disambiguating objects in rev-parse. + +## 0.30.0 (2022-06-13) + +A maintenance release without user-facing changes. + +## 0.29.0 (2022-05-18) + +A maintenance release without user-facing changes. + +## 0.28.0 (2022-04-05) + +### New Features + + - <csr-id-84ec54e904378c5b3d7da9efff66b02e88b16916/> Handle::packed_object_count() + Provide packed objects numbers and cache the value + for fast access later on. + +### Changed (BREAKING) + + - <csr-id-8c5ae77f06a64c57df9a9ad1190266896a223dbe/> Remove deprecated compound and linked object databases + The dynamic/general store is the only maintained can-do-it-all + DB now. + +## 0.27.0 (2022-04-03) + +- fixes a race condition around the first initialization of an ODB, which could leave the loosing + thread without any packs are loose databases. +- Adds support for lookups by prefix. + +### New Features + + - <csr-id-996bfb3061fd9ee2cf38c93f39e0d4c7c6163386/> loose::Store::lookup_prefix(…) + +### Bug Fixes + + - <csr-id-9c14de391a1a9f1055922164d1757c9aa9720807/> support Rust 1.52 + +## 0.26.0 (2022-01-23) + +<csr-id-ebc7f47708a63c3df4415ba0e702660d976dfb3e/> +<csr-id-2290d006705ff47ad780b009fe58ee422b3285af/> +<csr-id-e0b8636f96e4bfe1bc72b5aa6ad4c4c8538ff92c/> +<csr-id-2d6960f886c1165f0bdb6f2d653388e1e0b57a2d/> +<csr-id-424c9b3a2b467f5a1e339700257cd4ab72e2e692/> +<csr-id-b1c82a7959fba1541642fc8dfae46b27848f2ba3/> +<csr-id-9235106986e14551a28693bfe4ea92f046c65406/> +<csr-id-c800fdd331e6d7a0b8d756ba822915259f26e9e8/> + +### Refactor + + - <csr-id-e0b8636f96e4bfe1bc72b5aa6ad4c4c8538ff92c/> replace bare u32 `data::Id` typedef + +### Other + + - <csr-id-2d6960f886c1165f0bdb6f2d653388e1e0b57a2d/> try LRU-like contains implementation + Which unfortunately isn't really faster at all even though it totally + should be. + - <csr-id-424c9b3a2b467f5a1e339700257cd4ab72e2e692/> Try to make Handle usable for pack creation + It's nearly there, but for some reason the boxed dyn traits don't get to + be Send even though it's specified. + - <csr-id-b1c82a7959fba1541642fc8dfae46b27848f2ba3/> :Find for Arc and Rc + - <csr-id-9235106986e14551a28693bfe4ea92f046c65406/> :Find implementation for linked::Store + +### Chore + + - <csr-id-c800fdd331e6d7a0b8d756ba822915259f26e9e8/> remove unused dependencies + +### New Features + + - <csr-id-58c2edb76755ab71e10eef4cd9a51533825c291f/> gix_pack::Find::try_find_cached(…, pack_cache) + With this method it's easier to bypass local caches and control + the cache oneself entirely. + - <csr-id-36fde720c34e02429a810ddd43b894a37516f51a/> add linked::Store::rc_iter() + For completeness in case of single-threaded operations + - <csr-id-a81b33359a4394a66f854195445f8f9aa0a46179/> linked::Store sorts bundles by modification date, newest first + - <csr-id-e25f4eadec679406aad6df10026e27e4832c2482/> A simplified version of the `Find` trait + It's meant for the next generation of object db handles which keep a + local cache of all the details of the actual object database. + +### Bug Fixes + + - <csr-id-9c14de391a1a9f1055922164d1757c9aa9720807/> support Rust 1.52 + - <csr-id-b605c1fa0494b10872d3c2e6ecce0e39f1a90a9e/> linked::Store now assures unique IDs across compound stores + +### Changed (BREAKING) + + - <csr-id-91d047658b114f372735116c9d8e6962a3873137/> cleanup and unify `verify_integrity()` method signature + Previously they used many different ways of handling their parameters + despite all boiling down to calling the same 'index::File::traverse()` + method. + + This allows for more reuse of `Options` structs and generally makes + clearer how these options are used. + - <csr-id-2ef9a8424af51310db8c1e6df31dde9953ed3d21/> Change accessors named `hash_kind()` to `object_hash()` for consistency + - <csr-id-49998cce419a27f3928ec4ac39da5e3b500e5cb2/> consistently use `object_hash` instead of `hash_kind` + - <csr-id-67c42fbf5f88f8dc42a9ebd7c6276d57ba1d4624/> remove `Write::*(…, hash_kind)` + The `hash_kind` is now intrinsic to the implementation of the write + trait and thus isn't passed along anymore in parameters. + + The `sink()` function now takes the kind of hash as parameter. + - <csr-id-ad1b9ea17eb4b98ebd2fddebe82a8fee1d63e9dd/> various changes to the `loose::Store`. + + Change `path` field to read-only `path()` method + add `hash_kind` parameter to `loose::Store::at(…, hash_kind)` + - <csr-id-ab4e726fcec65871a81056a9c69af8ea3f56b2a3/> move `sink::Sink` to the top-level exclusively + - <csr-id-8bb5c9a75cd91ae0d888bc8e93707cfc9cc08090/> move `loose::iter::Iter` to `loose::Iter` + - <csr-id-3f05fea55dc8acce1ed62ecbe4e0a1394f2720b7/> remove `make_object_cache` parameter from `gix_pack::data::output::count::objects()` + It now is an implementation detail of the Find trait. + - <csr-id-580e96c1b2d9782a2e8cf9d1123f6d53a5376a3d/> Rename `Handle` to `Cache` + Because this is exactly what it is effectively. + Also add some basic instantiation for the new object store. + - remove pack-cache from `Find::try_find(…)` + With the new architecture this can be an implementation detail without + forcing it to be Sync. + - move gix_pack::data::Object to gix_object::Data, massively alter gix_odb::Find trait + This will break a lot, but has to happen to prepare these traits for the + next generation of object databases. + +### New Features (BREAKING) + + - <csr-id-bf73a94b43288b6634dbb33f2433656987a73baf/> `Cache::inner` removed in favor of `Deref/Mut` and `into_inner()` + Making the `inner` field available allows changing it, which would make + it potentially incompatible with existing caches. The new + implementation makes it essentially read-only while allowing more + convenient access to methods on `inner`. + +## 0.25.0 (2021-11-29) + +<csr-id-598698b88c194bc0e6ef69539f9fa7246ebfab70/> + +Maintenance release due, which isn't really required but one now has to be careful what's committed at once. + +### Refactor (BREAKING) + + - move loose header manipulation from gix-pack to gix-object + +## 0.24.0 (2021-11-16) + +A maintenance release triggered by changes to gix-pack and changelog rewrites. + +## v0.23.0 (2021-10-19) + +A maintenance release to properly dealing with previously breaking changes in `gix-hash`. + +## v0.22.0 (2021-10-15) + +### Dependency Upgrade (BREAKING) + +* `gix-traverse` saw a breaking change moving to v0.9, which triggered this crate to signal a breaking change, too. + +### Improvements + +* pack-ids as generated when instantiating a compound database are now sequential. That way, they never clash which + was possible before when it was using a CRC32 over the filename. + + The latter was done to allow deduplicating packs, but it turns out not to be necessary just yet. + +## v0.21.3 (2021-09-10) + +## v0.21.2 (2021-09-08) + +## v0.21.1 (2021-09-07) + +## v0.21.0 (2021-08-27) + +## v0.20.2 (2021-08-17) + +## v0.20.1 (2021-08-13) + +## v0.20.0 (2021-08-12) + +## v0.18.0 (2021-08-11) + +## v0.17.0 (2021-08-11) + +## v0.16.1 (2021-08-10) + +## v0.16.0 (2021-08-10) + +## v0.15.0 (2021-05-09) + +## v0.14.0 (2021-05-02) + +## v0.12.0 (2021-04-30) + +<csr-id-747a13e9a1fe5200c53055dd961507c9fef667e1/> +<csr-id-4c77e4c97641ab3b02b56aaa702a7d2ca5bced7c/> + +### Other + + - <csr-id-747a13e9a1fe5200c53055dd961507c9fef667e1/> :borrowed::Object => gix-odb::data::Object + - <csr-id-4c77e4c97641ab3b02b56aaa702a7d2ca5bced7c/> :Db::init() with a few tests + +## v0.10.0 (2021-04-08) + +<csr-id-d53c4b0f91f1b29769c9430f2d1c0bcab1170c75/> +<csr-id-b317200b72096573d511d229c6e61e74e7ba14db/> +<csr-id-eaae9c1bc723209d793eb93f5587fa2604d5cd92/> + +### Other + + - <csr-id-d53c4b0f91f1b29769c9430f2d1c0bcab1170c75/> add link to simplified/polonius version in the docs + - <csr-id-b317200b72096573d511d229c6e61e74e7ba14db/> Only check alternates for objects not found in packs or loose + This matches the behavior of git. + - <csr-id-eaae9c1bc723209d793eb93f5587fa2604d5cd92/> Avoid double-lookup in packs without polonius + Split object lookup into two steps: looking up the object index, and + looking up the object itself given the index. This avoids passing in the + buffer (and thus looking like an unconditional borrow to non-polonius) + until we're committed to returning from the loop. + +## v0.9.1 (2021-04-03) + +## v0.9.0 (2021-03-29) + +## v0.8.0 (2021-01-24) + +## v0.7.1 (2021-01-24) + +## v0.7.0 (2020-12-16) + +## v0.6.0 (2020-12-15) + +## v0.5.0 (2020-12-15) + +## v0.4.2 (2020-11-18) + +<csr-id-13159eb972ed78ce4ebee2313b288023cec91c47/> + +### Other + + - <csr-id-13159eb972ed78ce4ebee2313b288023cec91c47/> try to get rid of tree-traversal Boxed error… + …which really complicates things downstream as these now have to deal + with another type argument, or of to try to turn it into a Box anyway. + + The latter seems to be…troubling so I can't make it compile. + +## v0.4.1 (2020-09-18) + +<csr-id-0092c256b3bfaf2818566540e660cdefcf68d246/> +<csr-id-13159eb972ed78ce4ebee2313b288023cec91c47/> + +### Other + + - <csr-id-0092c256b3bfaf2818566540e660cdefcf68d246/> See if tree compaction saves considerable amounts of memory + No, it's not worth it. + +### Other + + - <csr-id-13159eb972ed78ce4ebee2313b288023cec91c47/> try to get rid of tree-traversal Boxed error… + …which really complicates things downstream as these now have to deal + with another type argument, or of to try to turn it into a Box anyway. + + The latter seems to be…troubling so I can't make it compile. + +## v0.4.0 (2020-09-12) + +<csr-id-0092c256b3bfaf2818566540e660cdefcf68d246/> + +### Other + + - <csr-id-0092c256b3bfaf2818566540e660cdefcf68d246/> See if tree compaction saves considerable amounts of memory + No, it's not worth it. + +## v0.3.0 (2020-08-12) + +<csr-id-5d57c1f7e3b9a84f7b46a4378015572155f3104b/> +<csr-id-9945eba749afb020e0deaaa5bb01fda6ff9ccd84/> +<csr-id-cfd8a25f9125c48afe4b66eab6b6ecf71097c486/> +<csr-id-1525f36d29574699d2fcb16b70678121030fd109/> + +### Refactor + + - <csr-id-5d57c1f7e3b9a84f7b46a4378015572155f3104b/> Use borrowed::Id in trees for full type safety + +### Other + + - <csr-id-9945eba749afb020e0deaaa5bb01fda6ff9ccd84/> try to use a customized version of just pieces of Miniz-oxide + - <csr-id-cfd8a25f9125c48afe4b66eab6b6ecf71097c486/> fanout table, but slowly I get it :D + - <csr-id-1525f36d29574699d2fcb16b70678121030fd109/> discard idea of making traversal even more generic + +## v0.1.0 (2020-07-12) + +<csr-id-47ca6ab2ff0cbf8801d0a82cebbbeb8c4f62cdae/> +<csr-id-4ff21686c32a6edc84ea041c3040f33ae24f9519/> +<csr-id-91c8fc1f0c50af55d7cb233bbe813c6d12fe11bc/> + +### Refactor + + - <csr-id-47ca6ab2ff0cbf8801d0a82cebbbeb8c4f62cdae/> a simpler implementation to skip the header + +### Other + + - <csr-id-4ff21686c32a6edc84ea041c3040f33ae24f9519/> first silly attempt to randomly remove an allocation + - <csr-id-91c8fc1f0c50af55d7cb233bbe813c6d12fe11bc/> get rid of failure crate in favor of quick-error + diff --git a/vendor/gix-odb/Cargo.toml b/vendor/gix-odb/Cargo.toml new file mode 100644 index 000000000..3902cf5d8 --- /dev/null +++ b/vendor/gix-odb/Cargo.toml @@ -0,0 +1,114 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.64" +name = "gix-odb" +version = "0.42.0" +authors = ["Sebastian Thiel <sebastian.thiel@icloud.com>"] +include = [ + "src/**/*", + "CHANGELOG.md", +] +description = "Implements various git object databases" +license = "MIT/Apache-2.0" +repository = "https://github.com/Byron/gitoxide" + +[package.metadata.docs.rs] +features = [ + "document-features", + "serde1", +] +rustdoc-args = [ + "--cfg", + "docsrs", +] + +[lib] +doctest = false + +[[test]] +name = "multi-threaded" +path = "tests/odb-multi-threaded.rs" +required-features = ["internal-testing-gix-features-parallel"] + +[[test]] +name = "single-threaded" +path = "tests/odb-single-threaded.rs" +required-features = [] + +[dependencies.arc-swap] +version = "1.5.0" + +[dependencies.document-features] +version = "0.2.0" +optional = true + +[dependencies.gix-features] +version = "^0.28.0" +features = [ + "rustsha1", + "walkdir", + "zlib", + "crc32", +] + +[dependencies.gix-hash] +version = "^0.10.2" + +[dependencies.gix-object] +version = "^0.28.0" + +[dependencies.gix-pack] +version = "^0.32.0" + +[dependencies.gix-path] +version = "^0.7.1" + +[dependencies.gix-quote] +version = "^0.4.3" + +[dependencies.parking_lot] +version = "0.12.0" + +[dependencies.serde] +version = "1.0.114" +features = ["derive"] +optional = true +default-features = false + +[dependencies.tempfile] +version = "3.1.0" + +[dependencies.thiserror] +version = "1.0.26" + +[dev-dependencies.crossbeam-channel] +version = "0.5.6" + +[dev-dependencies.filetime] +version = "0.2.15" + +[dev-dependencies.maplit] +version = "1.0.2" + +[dev-dependencies.pretty_assertions] +version = "1.0.0" + +[features] +internal-testing-gix-features-parallel = ["gix-features/parallel"] +serde1 = [ + "serde", + "gix-hash/serde1", + "gix-object/serde1", + "gix-pack/serde1", +] diff --git a/vendor/gix-odb/src/alternate/mod.rs b/vendor/gix-odb/src/alternate/mod.rs new file mode 100644 index 000000000..c343ef5aa --- /dev/null +++ b/vendor/gix-odb/src/alternate/mod.rs @@ -0,0 +1,75 @@ +//! A file with directories of other git object databases to use when reading objects. +//! +//! This inherently makes alternates read-only. +//! +//! An alternate file in `<git-dir>/info/alternates` can look as follows: +//! +//! ```text +//! # a comment, empty lines are also allowed +//! # relative paths resolve relative to the parent git repository +//! ../path/relative/to/repo/.git +//! /absolute/path/to/repo/.git +//! +//! "/a/ansi-c-quoted/path/with/tabs\t/.git" +//! +//! # each .git directory should indeed be a directory, and not a file +//! ``` +//! +//! Based on the [canonical implementation](https://github.com/git/git/blob/master/sha1-file.c#L598:L609). +use std::{fs, io, path::PathBuf}; + +use gix_path::realpath::MAX_SYMLINKS; + +/// +pub mod parse; + +/// Returned by [`resolve()`] +#[derive(thiserror::Error, Debug)] +#[allow(missing_docs)] +pub enum Error { + #[error(transparent)] + Io(#[from] io::Error), + #[error(transparent)] + Realpath(#[from] gix_path::realpath::Error), + #[error(transparent)] + Parse(#[from] parse::Error), + #[error("Alternates form a cycle: {} -> {}", .0.iter().map(|p| format!("'{}'", p.display())).collect::<Vec<_>>().join(" -> "), .0.first().expect("more than one directories").display())] + Cycle(Vec<PathBuf>), +} + +/// Given an `objects_directory`, try to resolve alternate object directories possibly located in the +/// `./info/alternates` file into canonical paths and resolve relative paths with the help of the `current_dir`. +/// If no alternate object database was resolved, the resulting `Vec` is empty (it is not an error +/// if there are no alternates). +/// It is an error once a repository is seen again as it would lead to a cycle. +pub fn resolve( + objects_directory: impl Into<PathBuf>, + current_dir: impl AsRef<std::path::Path>, +) -> Result<Vec<PathBuf>, Error> { + let relative_base = objects_directory.into(); + let mut dirs = vec![(0, relative_base.clone())]; + let mut out = Vec::new(); + let cwd = current_dir.as_ref(); + let mut seen = vec![gix_path::realpath_opts(&relative_base, cwd, MAX_SYMLINKS)?]; + while let Some((depth, dir)) = dirs.pop() { + match fs::read(dir.join("info").join("alternates")) { + Ok(input) => { + for path in parse::content(&input)?.into_iter() { + let path = relative_base.join(path); + let path_canonicalized = gix_path::realpath_opts(&path, cwd, MAX_SYMLINKS)?; + if seen.contains(&path_canonicalized) { + return Err(Error::Cycle(seen)); + } + seen.push(path_canonicalized); + dirs.push((depth + 1, path)); + } + } + Err(err) if err.kind() == io::ErrorKind::NotFound => {} + Err(err) => return Err(err.into()), + }; + if depth != 0 { + out.push(dir); + } + } + Ok(out) +} diff --git a/vendor/gix-odb/src/alternate/parse.rs b/vendor/gix-odb/src/alternate/parse.rs new file mode 100644 index 000000000..1c297d153 --- /dev/null +++ b/vendor/gix-odb/src/alternate/parse.rs @@ -0,0 +1,33 @@ +use std::{borrow::Cow, path::PathBuf}; + +use gix_object::bstr::ByteSlice; + +/// Returned as part of [`crate::alternate::Error::Parse`] +#[derive(thiserror::Error, Debug)] +#[allow(missing_docs)] +pub enum Error { + #[error("Could not obtain an object path for the alternate directory '{}'", String::from_utf8_lossy(.0))] + PathConversion(Vec<u8>), + #[error("Could not unquote alternate path")] + Unquote(#[from] gix_quote::ansi_c::undo::Error), +} + +pub(crate) fn content(input: &[u8]) -> Result<Vec<PathBuf>, Error> { + let mut out = Vec::new(); + for line in input.split(|b| *b == b'\n') { + let line = line.as_bstr(); + if line.is_empty() || line.starts_with(b"#") { + continue; + } + out.push( + gix_path::try_from_bstr(if line.starts_with(b"\"") { + gix_quote::ansi_c::undo(line)?.0 + } else { + Cow::Borrowed(line) + }) + .map_err(|_| Error::PathConversion(line.to_vec()))? + .into_owned(), + ) + } + Ok(out) +} diff --git a/vendor/gix-odb/src/cache.rs b/vendor/gix-odb/src/cache.rs new file mode 100644 index 000000000..8e108646f --- /dev/null +++ b/vendor/gix-odb/src/cache.rs @@ -0,0 +1,234 @@ +use std::{ + cell::RefCell, + ops::{Deref, DerefMut}, + rc::Rc, + sync::Arc, +}; + +use crate::Cache; + +/// A type to store pack caches in boxes. +pub type PackCache = dyn gix_pack::cache::DecodeEntry + Send + 'static; +/// A constructor for boxed pack caches. +pub type NewPackCacheFn = dyn Fn() -> Box<PackCache> + Send + Sync + 'static; + +/// A type to store object caches in boxes. +pub type ObjectCache = dyn gix_pack::cache::Object + Send + 'static; +/// A constructor for boxed object caches. +pub type NewObjectCacheFn = dyn Fn() -> Box<ObjectCache> + Send + Sync + 'static; + +impl Cache<crate::store::Handle<Rc<crate::Store>>> { + /// Convert this cache's handle into one that keeps its store in an arc. This creates an entirely new store, + /// so should be done early to avoid unnecessary work (and mappings). + pub fn into_arc(self) -> std::io::Result<Cache<crate::store::Handle<Arc<crate::Store>>>> { + let inner = self.inner.into_arc()?; + Ok(Cache { + inner, + new_pack_cache: self.new_pack_cache, + new_object_cache: self.new_object_cache, + pack_cache: self.pack_cache, + object_cache: self.object_cache, + }) + } +} +impl Cache<crate::store::Handle<Arc<crate::Store>>> { + /// No op, as we are containing an arc handle already. + pub fn into_arc(self) -> std::io::Result<Cache<crate::store::Handle<Arc<crate::Store>>>> { + Ok(self) + } +} + +impl<S> Cache<S> { + /// Dissolve this instance, discard all caches, and return the inner implementation. + pub fn into_inner(self) -> S { + self.inner + } + /// Use this methods directly after creating a new instance to add a constructor for pack caches. + /// + /// These are used to speed up decoding objects which are located in packs, reducing long delta chains by storing + /// their intermediate results. + pub fn with_pack_cache(mut self, create: impl Fn() -> Box<PackCache> + Send + Sync + 'static) -> Self { + self.pack_cache = Some(RefCell::new(create())); + self.new_pack_cache = Some(Arc::new(create)); + self + } + /// Use this methods directly after creating a new instance to add a constructor for object caches. + /// + /// Only use this kind of cache if the same objects are repeatedly accessed for great speedups, usually during diffing of + /// trees. + pub fn with_object_cache(mut self, create: impl Fn() -> Box<ObjectCache> + Send + Sync + 'static) -> Self { + self.object_cache = Some(RefCell::new(create())); + self.new_object_cache = Some(Arc::new(create)); + self + } + /// Set the pack cache constructor on this instance. + pub fn set_pack_cache(&mut self, create: impl Fn() -> Box<PackCache> + Send + Sync + 'static) { + self.pack_cache = Some(RefCell::new(create())); + self.new_pack_cache = Some(Arc::new(create)); + } + /// Set the object cache constructor on this instance. + pub fn set_object_cache(&mut self, create: impl Fn() -> Box<ObjectCache> + Send + Sync + 'static) { + self.object_cache = Some(RefCell::new(create())); + self.new_object_cache = Some(Arc::new(create)); + } + /// Return true if an object cache is present. + pub fn has_object_cache(&self) -> bool { + self.object_cache.is_some() + } + /// Return true if a pack cache is present. + pub fn has_pack_cache(&self) -> bool { + self.pack_cache.is_some() + } + /// Remove the current pack cache as well as its constructor from this instance. + pub fn unset_pack_cache(&mut self) { + self.pack_cache = None; + self.new_pack_cache = None; + } + /// Remove the current object cache as well as its constructor from this instance. + pub fn unset_object_cache(&mut self) { + self.object_cache = None; + self.new_object_cache = None; + } +} + +impl<S> From<S> for Cache<S> +where + S: gix_pack::Find, +{ + fn from(store: S) -> Self { + Self { + inner: store, + pack_cache: None, + new_pack_cache: None, + object_cache: None, + new_object_cache: None, + } + } +} + +impl<S: Clone> Clone for Cache<S> { + fn clone(&self) -> Self { + Cache { + inner: self.inner.clone(), + new_pack_cache: self.new_pack_cache.clone(), + new_object_cache: self.new_object_cache.clone(), + pack_cache: self.new_pack_cache.as_ref().map(|create| RefCell::new(create())), + object_cache: self.new_object_cache.as_ref().map(|create| RefCell::new(create())), + } + } +} + +impl<S> Deref for Cache<S> { + type Target = S; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl<S> DerefMut for Cache<S> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +mod impls { + use std::{io::Read, ops::DerefMut}; + + use gix_hash::{oid, ObjectId}; + use gix_object::{Data, Kind}; + use gix_pack::cache::Object; + + use crate::{find::Header, pack::data::entry::Location, Cache}; + + impl<S> crate::Write for Cache<S> + where + S: crate::Write, + { + type Error = S::Error; + + fn write_stream(&self, kind: Kind, size: u64, from: impl Read) -> Result<ObjectId, Self::Error> { + self.inner.write_stream(kind, size, from) + } + } + + impl<S> crate::Find for Cache<S> + where + S: gix_pack::Find, + { + type Error = S::Error; + + fn contains(&self, id: impl AsRef<oid>) -> bool { + self.inner.contains(id) + } + + fn try_find<'a>(&self, id: impl AsRef<oid>, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>, Self::Error> { + gix_pack::Find::try_find(self, id, buffer).map(|t| t.map(|t| t.0)) + } + } + + impl<S> crate::Header for Cache<S> + where + S: crate::Header, + { + type Error = S::Error; + + fn try_header(&self, id: impl AsRef<oid>) -> Result<Option<Header>, Self::Error> { + self.inner.try_header(id) + } + } + + impl<S> gix_pack::Find for Cache<S> + where + S: gix_pack::Find, + { + type Error = S::Error; + + fn contains(&self, id: impl AsRef<oid>) -> bool { + self.inner.contains(id) + } + + fn try_find<'a>( + &self, + id: impl AsRef<oid>, + buffer: &'a mut Vec<u8>, + ) -> Result<Option<(Data<'a>, Option<Location>)>, Self::Error> { + match self.pack_cache.as_ref().map(|rc| rc.borrow_mut()) { + Some(mut pack_cache) => self.try_find_cached(id, buffer, pack_cache.deref_mut()), + None => self.try_find_cached(id, buffer, &mut gix_pack::cache::Never), + } + } + + fn try_find_cached<'a>( + &self, + id: impl AsRef<oid>, + buffer: &'a mut Vec<u8>, + pack_cache: &mut impl gix_pack::cache::DecodeEntry, + ) -> Result<Option<(Data<'a>, Option<gix_pack::data::entry::Location>)>, Self::Error> { + if let Some(mut obj_cache) = self.object_cache.as_ref().map(|rc| rc.borrow_mut()) { + if let Some(kind) = obj_cache.get(&id.as_ref().to_owned(), buffer) { + return Ok(Some((Data::new(kind, buffer), None))); + } + } + let possibly_obj = self.inner.try_find_cached(id.as_ref(), buffer, pack_cache)?; + if let (Some(mut obj_cache), Some((obj, _location))) = + (self.object_cache.as_ref().map(|rc| rc.borrow_mut()), &possibly_obj) + { + obj_cache.put(id.as_ref().to_owned(), obj.kind, obj.data); + } + Ok(possibly_obj) + } + + fn location_by_oid(&self, id: impl AsRef<oid>, buf: &mut Vec<u8>) -> Option<gix_pack::data::entry::Location> { + self.inner.location_by_oid(id, buf) + } + + fn pack_offsets_and_oid(&self, pack_id: u32) -> Option<Vec<(u64, gix_hash::ObjectId)>> { + self.inner.pack_offsets_and_oid(pack_id) + } + + fn entry_by_location(&self, location: &Location) -> Option<gix_pack::find::Entry> { + self.inner.entry_by_location(location) + } + } +} diff --git a/vendor/gix-odb/src/find.rs b/vendor/gix-odb/src/find.rs new file mode 100644 index 000000000..69eccbf04 --- /dev/null +++ b/vendor/gix-odb/src/find.rs @@ -0,0 +1,113 @@ +/// +pub mod existing { + use gix_hash::ObjectId; + + /// The error returned by the [`find(…)`][crate::FindExt::find()] trait methods. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error<T: std::error::Error + 'static> { + #[error(transparent)] + Find(T), + #[error("An object with id {} could not be found", .oid)] + NotFound { oid: ObjectId }, + } +} + +/// +pub mod existing_object { + use gix_hash::ObjectId; + + /// The error returned by the various [`find_*()`][crate::FindExt::find_commit()] trait methods. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error<T: std::error::Error + 'static> { + #[error(transparent)] + Find(T), + #[error(transparent)] + Decode(gix_object::decode::Error), + #[error("An object with id {oid} could not be found")] + NotFound { oid: ObjectId }, + #[error("Expected object of kind {expected}")] + ObjectKind { expected: gix_object::Kind }, + } +} + +/// +pub mod existing_iter { + use gix_hash::ObjectId; + + /// The error returned by the various [`find_*_iter()`][crate::FindExt::find_commit_iter()] trait methods. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error<T: std::error::Error + 'static> { + #[error(transparent)] + Find(T), + #[error("An object with id {oid} could not be found")] + NotFound { oid: ObjectId }, + #[error("Expected object of kind {expected}")] + ObjectKind { expected: gix_object::Kind }, + } +} + +/// An object header informing about object properties, without it being fully decoded in the process. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum Header { + /// The object was not packed, but is currently located in the loose object portion of the database. + /// + /// As packs are searched first, this means that in this very moment, the object whose header we retrieved is unique + /// in the object database. + Loose { + /// The kind of the object. + kind: gix_object::Kind, + /// The size of the object's data in bytes. + size: u64, + }, + /// The object was present in a pack. + /// + /// Note that this does not imply it is unique in the database, as it might be present in more than one pack and even + /// as loose object. + Packed(gix_pack::data::decode::header::Outcome), +} + +mod header { + use super::Header; + + impl Header { + /// Return the object kind of the object we represent. + pub fn kind(&self) -> gix_object::Kind { + match self { + Header::Packed(out) => out.kind, + Header::Loose { kind, .. } => *kind, + } + } + /// Return the size of the object in bytes. + pub fn size(&self) -> u64 { + match self { + Header::Packed(out) => out.object_size, + Header::Loose { size, .. } => *size, + } + } + /// Return the amount of deltas decoded to obtain this header, if the object was packed. + pub fn num_deltas(&self) -> Option<u32> { + match self { + Header::Packed(out) => out.num_deltas.into(), + Header::Loose { .. } => None, + } + } + } + + impl From<gix_pack::data::decode::header::Outcome> for Header { + fn from(packed_header: gix_pack::data::decode::header::Outcome) -> Self { + Header::Packed(packed_header) + } + } + + impl From<(usize, gix_object::Kind)> for Header { + fn from((object_size, kind): (usize, gix_object::Kind)) -> Self { + Header::Loose { + kind, + size: object_size as u64, + } + } + } +} diff --git a/vendor/gix-odb/src/lib.rs b/vendor/gix-odb/src/lib.rs new file mode 100644 index 000000000..a63ea544f --- /dev/null +++ b/vendor/gix-odb/src/lib.rs @@ -0,0 +1,152 @@ +//! Git stores all of its data as _Objects_, which are data along with a hash over all data. Thus it's an +//! object store indexed by the signature of data itself with inherent deduplication: the same data will have the same hash, +//! and thus occupy the same space within the store. +//! +//! There is only one all-round object store, also known as the [`Store`], as it supports ~~everything~~ most of what git has to offer. +//! +//! * loose object reading and writing +//! * access to packed objects +//! * multiple loose objects and pack locations as gathered from `alternates` files. +//! ## Feature Flags +#![cfg_attr( + feature = "document-features", + cfg_attr(doc, doc = ::document_features::document_features!()) +)] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![deny(missing_docs, rust_2018_idioms, unsafe_code)] + +use std::{ + cell::RefCell, + path::PathBuf, + sync::{atomic::AtomicUsize, Arc}, +}; + +use arc_swap::ArcSwap; +use gix_features::{threading::OwnShared, zlib::stream::deflate}; +pub use gix_pack as pack; + +mod store_impls; +pub use store_impls::{dynamic as store, loose}; + +pub mod alternate; + +/// A way to access objects along with pre-configured thread-local caches for packed base objects as well as objects themselves. +/// +/// By default, no cache will be used. +pub struct Cache<S> { + /// The inner provider of trait implementations we use in conjunction with our caches. + /// + /// For calling methods on `inner`, prefer to make use of auto-dereferencing, i.e. `cache.inner_method()` instead of `cache.inner.inner_method()`. + inner: S, + // TODO: have single-threaded code-paths also for pack-creation (entries from counts) so that we can use OwnShared here + // instead of Arc. However, it's probably not that important as these aren't called often. + new_pack_cache: Option<Arc<cache::NewPackCacheFn>>, + new_object_cache: Option<Arc<cache::NewObjectCacheFn>>, + pack_cache: Option<RefCell<Box<cache::PackCache>>>, + object_cache: Option<RefCell<Box<cache::ObjectCache>>>, +} + +/// +pub mod cache; + +/// +/// It can optionally compress the content, similarly to what would happen when using a [`loose::Store`][crate::loose::Store]. +/// +pub struct Sink { + compressor: Option<RefCell<deflate::Write<std::io::Sink>>>, + object_hash: gix_hash::Kind, +} + +/// Create a new [`Sink`] with compression disabled. +pub fn sink(object_hash: gix_hash::Kind) -> Sink { + Sink { + compressor: None, + object_hash, + } +} + +/// +pub mod sink; + +/// +pub mod find; + +/// An object database equivalent to `/dev/null`, dropping all objects stored into it. +mod traits; + +pub use traits::{Find, FindExt, Header, HeaderExt, Write}; + +/// A thread-local handle to access any object. +pub type Handle = Cache<store::Handle<OwnShared<Store>>>; +/// A thread-local handle to access any object, but thread-safe and independent of the actual type of `OwnShared` or feature toggles in `gix-features`. +pub type HandleArc = Cache<store::Handle<Arc<Store>>>; + +use store::types; + +/// The object store for use in any applications with support for auto-updates in the light of changes to the object database. +/// +/// ### Features +/// +/// - entirely lazy, creating an instance does no disk IO at all if [`Slots::Given`][store::init::Slots::Given] is used. +/// - multi-threaded lazy-loading of indices and packs +/// - per-thread pack and object caching avoiding cache trashing. +/// - most-recently-used packs are always first for speedups if objects are stored in the same pack, typical for packs organized by +/// commit graph and object age. +/// - lock-free reading for perfect scaling across all cores, and changes to it don't affect readers as long as these don't want to +/// enter the same branch. +/// - sync with the state on disk if objects aren't found to catch up with changes if an object seems to be missing. +/// - turn off the behaviour above for all handles if objects are expected to be missing due to spare checkouts. +pub struct Store { + /// The central write lock without which the slotmap index can't be changed. + write: parking_lot::Mutex<()>, + + /// The source directory from which all content is loaded, and the central write lock for use when a directory refresh is needed. + pub(crate) path: PathBuf, + + /// The current working directory at the time this store was instantiated. It becomes relevant when resolving alternate paths + /// when re-reading the store configuration on updates when an object was missed. + /// Keeping it here helps to assure consistency even while a process changes its CWD. + pub(crate) current_dir: PathBuf, + + /// A set of replacements that given a source OID return a destination OID. The vector is sorted. + pub(crate) replacements: Vec<(gix_hash::ObjectId, gix_hash::ObjectId)>, + + /// A list of indices keeping track of which slots are filled with data. These are usually, but not always, consecutive. + pub(crate) index: ArcSwap<types::SlotMapIndex>, + + /// The below state acts like a slot-map with each slot is mutable when the write lock is held, but readable independently of it. + /// This allows multiple file to be loaded concurrently if there is multiple handles requesting to load packs or additional indices. + /// The map is static and cannot typically change. + /// It's read often and changed rarely. + pub(crate) files: Vec<types::MutableIndexAndPack>, + + /// The amount of handles that would prevent us from unloading packs or indices + pub(crate) num_handles_stable: AtomicUsize, + /// The amount of handles that don't affect our ability to compact our internal data structures or unload packs or indices. + pub(crate) num_handles_unstable: AtomicUsize, + + /// The amount of times we re-read the disk state to consolidate our in-memory representation. + pub(crate) num_disk_state_consolidation: AtomicUsize, + /// If true, we are allowed to use multi-pack indices and they must have the `object_hash` or be ignored. + use_multi_pack_index: bool, + /// The hash kind to use for some operations + object_hash: gix_hash::Kind, +} + +/// Create a new cached handle to the object store with support for additional options. +/// +/// `replacements` is an iterator over pairs of old and new object ids for replacement support. +/// This means that when asking for object `X`, one will receive object `X-replaced` given an iterator like `Some((X, X-replaced))`. +pub fn at_opts( + objects_dir: impl Into<PathBuf>, + replacements: impl IntoIterator<Item = (gix_hash::ObjectId, gix_hash::ObjectId)>, + options: store::init::Options, +) -> std::io::Result<Handle> { + let handle = OwnShared::new(Store::at_opts(objects_dir, replacements, options)?).to_handle(); + Ok(Cache::from(handle)) +} + +/// Create a new cached handle to the object store. +pub fn at(objects_dir: impl Into<PathBuf>) -> std::io::Result<Handle> { + at_opts(objects_dir, Vec::new().into_iter(), Default::default()) +} diff --git a/vendor/gix-odb/src/sink.rs b/vendor/gix-odb/src/sink.rs new file mode 100644 index 000000000..1befd6fdf --- /dev/null +++ b/vendor/gix-odb/src/sink.rs @@ -0,0 +1,66 @@ +use std::{ + cell::RefCell, + convert::TryInto, + io::{self, Write}, +}; + +use gix_features::zlib::stream::deflate; + +use crate::Sink; + +impl Sink { + /// Enable or disable compression. Compression is disabled by default + pub fn compress(mut self, enable: bool) -> Self { + if enable { + self.compressor = Some(RefCell::new(deflate::Write::new(io::sink()))); + } else { + self.compressor = None; + } + self + } +} + +impl crate::traits::Write for Sink { + type Error = io::Error; + + fn write_stream( + &self, + kind: gix_object::Kind, + size: u64, + mut from: impl io::Read, + ) -> Result<gix_hash::ObjectId, Self::Error> { + let mut size = size.try_into().expect("object size to fit into usize"); + use gix_features::hash::Sha1; + let mut buf = [0u8; 8096]; + let header = gix_object::encode::loose_header(kind, size); + + let possibly_compress = |buf: &[u8]| -> io::Result<()> { + if let Some(compressor) = self.compressor.as_ref() { + compressor.try_borrow_mut().expect("no recursion").write_all(buf)?; + } + Ok(()) + }; + match self.object_hash { + gix_hash::Kind::Sha1 => { + let mut hasher = Sha1::default(); + hasher.update(&header); + possibly_compress(&header)?; + + while size != 0 { + let bytes = size.min(buf.len()); + from.read_exact(&mut buf[..bytes])?; + hasher.update(&buf[..bytes]); + possibly_compress(&buf[..bytes])?; + size -= bytes; + } + if let Some(compressor) = self.compressor.as_ref() { + let mut c = compressor.borrow_mut(); + c.flush()?; + c.reset(); + } + + Ok(hasher.digest().into()) + } + } + } +} diff --git a/vendor/gix-odb/src/store_impls/dynamic/access.rs b/vendor/gix-odb/src/store_impls/dynamic/access.rs new file mode 100644 index 000000000..7a07bcfef --- /dev/null +++ b/vendor/gix-odb/src/store_impls/dynamic/access.rs @@ -0,0 +1,24 @@ +use crate::Store; + +impl Store { + /// The root path at which we expect to find all objects and packs, and which is the source of the + /// alternate file traversal in case there are linked repositories. + pub fn path(&self) -> &std::path::Path { + &self.path + } + + /// The kind of object hash to assume when dealing with pack indices and pack data files. + pub fn object_hash(&self) -> gix_hash::Kind { + self.object_hash + } + + /// Whether or not we are allowed to use multi-pack indices + pub fn use_multi_pack_index(&self) -> bool { + self.use_multi_pack_index + } + + /// An iterator over replacements from object-ids `X` to `X-replaced` as `(X, X-replaced)`, sorted by the original id `X`. + pub fn replacements(&self) -> impl Iterator<Item = (gix_hash::ObjectId, gix_hash::ObjectId)> + '_ { + self.replacements.iter().cloned() + } +} diff --git a/vendor/gix-odb/src/store_impls/dynamic/find.rs b/vendor/gix-odb/src/store_impls/dynamic/find.rs new file mode 100644 index 000000000..b6fa3b312 --- /dev/null +++ b/vendor/gix-odb/src/store_impls/dynamic/find.rs @@ -0,0 +1,519 @@ +use std::{convert::TryInto, ops::Deref}; + +use gix_pack::cache::DecodeEntry; + +use crate::store::{handle, load_index}; + +pub(crate) mod error { + use crate::{loose, pack}; + + /// Returned by [`Handle::try_find()`][gix_pack::Find::try_find()] + #[derive(thiserror::Error, Debug)] + #[allow(missing_docs)] + pub enum Error { + #[error("An error occurred while obtaining an object from the loose object store")] + Loose(#[from] loose::find::Error), + #[error("An error occurred while obtaining an object from the packed object store")] + Pack(#[from] pack::data::decode::Error), + #[error(transparent)] + LoadIndex(#[from] crate::store::load_index::Error), + #[error(transparent)] + LoadPack(#[from] std::io::Error), + #[error("Reached recursion limit of {} while resolving ref delta bases for {}", .max_depth, .id)] + DeltaBaseRecursionLimit { + /// the maximum recursion depth we encountered. + max_depth: usize, + /// The original object to lookup + id: gix_hash::ObjectId, + }, + #[error("The base object {} could not be found but is required to decode {}", .base_id, .id)] + DeltaBaseMissing { + /// the id of the base object which failed to lookup + base_id: gix_hash::ObjectId, + /// The original object to lookup + id: gix_hash::ObjectId, + }, + #[error("An error occurred when looking up a ref delta base object {} to decode {}", .base_id, .id)] + DeltaBaseLookup { + #[source] + err: Box<Self>, + /// the id of the base object which failed to lookup + base_id: gix_hash::ObjectId, + /// The original object to lookup + id: gix_hash::ObjectId, + }, + } + + #[derive(Copy, Clone)] + pub(crate) struct DeltaBaseRecursion<'a> { + pub depth: usize, + pub original_id: &'a gix_hash::oid, + } + + impl<'a> DeltaBaseRecursion<'a> { + pub fn new(id: &'a gix_hash::oid) -> Self { + Self { + original_id: id, + depth: 0, + } + } + pub fn inc_depth(mut self) -> Self { + self.depth += 1; + self + } + } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + fn error_size() { + let actual = std::mem::size_of::<Error>(); + assert!(actual <= 88, "{actual} <= 88: should not grow without us noticing"); + } + } +} +pub use error::Error; + +use crate::{store::types::PackId, Find}; + +impl<S> super::Handle<S> +where + S: Deref<Target = super::Store> + Clone, +{ + fn try_find_cached_inner<'a, 'b>( + &'b self, + mut id: &'b gix_hash::oid, + buffer: &'a mut Vec<u8>, + pack_cache: &mut impl DecodeEntry, + snapshot: &mut load_index::Snapshot, + recursion: Option<error::DeltaBaseRecursion<'_>>, + ) -> Result<Option<(gix_object::Data<'a>, Option<gix_pack::data::entry::Location>)>, Error> { + if let Some(r) = recursion { + if r.depth >= self.max_recursion_depth { + return Err(Error::DeltaBaseRecursionLimit { + max_depth: self.max_recursion_depth, + id: r.original_id.to_owned(), + }); + } + } else if !self.ignore_replacements { + if let Ok(pos) = self + .store + .replacements + .binary_search_by(|(map_this, _)| map_this.as_ref().cmp(id)) + { + id = self.store.replacements[pos].1.as_ref(); + } + } + + 'outer: loop { + { + let marker = snapshot.marker; + for (idx, index) in snapshot.indices.iter_mut().enumerate() { + if let Some(handle::index_lookup::Outcome { + object_index: handle::IndexForObjectInPack { pack_id, pack_offset }, + index_file, + pack: possibly_pack, + }) = index.lookup(id) + { + let pack = match possibly_pack { + Some(pack) => pack, + None => match self.store.load_pack(pack_id, marker)? { + Some(pack) => { + *possibly_pack = Some(pack); + possibly_pack.as_deref().expect("just put it in") + } + None => { + // The pack wasn't available anymore so we are supposed to try another round with a fresh index + match self.store.load_one_index(self.refresh, snapshot.marker)? { + Some(new_snapshot) => { + *snapshot = new_snapshot; + self.clear_cache(); + continue 'outer; + } + None => { + // nothing new in the index, kind of unexpected to not have a pack but to also + // to have no new index yet. We set the new index before removing any slots, so + // this should be observable. + return Ok(None); + } + } + } + }, + }; + let entry = pack.entry(pack_offset); + let header_size = entry.header_size(); + let res = match pack.decode_entry( + entry, + buffer, + |id, _out| { + index_file.pack_offset_by_id(id).map(|pack_offset| { + gix_pack::data::decode::entry::ResolvedBase::InPack(pack.entry(pack_offset)) + }) + }, + pack_cache, + ) { + Ok(r) => Ok(( + gix_object::Data { + kind: r.kind, + data: buffer.as_slice(), + }, + Some(gix_pack::data::entry::Location { + pack_id: pack.id, + pack_offset, + entry_size: r.compressed_size + header_size, + }), + )), + Err(gix_pack::data::decode::Error::DeltaBaseUnresolved(base_id)) => { + // Only with multi-pack indices it's allowed to jump to refer to other packs within this + // multi-pack. Otherwise this would constitute a thin pack which is only allowed in transit. + // However, if we somehow end up with that, we will resolve it safely, even though we could + // avoid handling this case and error instead. + + // Since this is a special case, we just allocate here to make it work. It's an actual delta-ref object + // which is sent by some servers that points to an object outside of the pack we are looking + // at right now. With the complexities of loading packs, we go into recursion here. Git itself + // doesn't do a cycle check, and we won't either but limit the recursive depth. + // The whole ordeal isn't as efficient as it could be due to memory allocation and + // later mem-copying when trying again. + let mut buf = Vec::new(); + let obj_kind = self + .try_find_cached_inner( + &base_id, + &mut buf, + pack_cache, + snapshot, + recursion + .map(|r| r.inc_depth()) + .or_else(|| error::DeltaBaseRecursion::new(id).into()), + ) + .map_err(|err| Error::DeltaBaseLookup { + err: Box::new(err), + base_id, + id: id.to_owned(), + })? + .ok_or_else(|| Error::DeltaBaseMissing { + base_id, + id: id.to_owned(), + })? + .0 + .kind; + let handle::index_lookup::Outcome { + object_index: + handle::IndexForObjectInPack { + pack_id: _, + pack_offset, + }, + index_file, + pack: possibly_pack, + } = match snapshot.indices[idx].lookup(id) { + Some(res) => res, + None => { + let mut out = None; + for index in snapshot.indices.iter_mut() { + out = index.lookup(id); + if out.is_some() { + break; + } + } + + out.unwrap_or_else(|| { + panic!("could not find object {id} in any index after looking up one of its base objects {base_id}" ) + }) + } + }; + let pack = possibly_pack + .as_ref() + .expect("pack to still be available like just now"); + let entry = pack.entry(pack_offset); + let header_size = entry.header_size(); + pack.decode_entry( + entry, + buffer, + |id, out| { + index_file + .pack_offset_by_id(id) + .map(|pack_offset| { + gix_pack::data::decode::entry::ResolvedBase::InPack( + pack.entry(pack_offset), + ) + }) + .or_else(|| { + (id == base_id).then(|| { + out.resize(buf.len(), 0); + out.copy_from_slice(buf.as_slice()); + gix_pack::data::decode::entry::ResolvedBase::OutOfPack { + kind: obj_kind, + end: out.len(), + } + }) + }) + }, + pack_cache, + ) + .map(move |r| { + ( + gix_object::Data { + kind: r.kind, + data: buffer.as_slice(), + }, + Some(gix_pack::data::entry::Location { + pack_id: pack.id, + pack_offset, + entry_size: r.compressed_size + header_size, + }), + ) + }) + } + Err(err) => Err(err), + }?; + + if idx != 0 { + snapshot.indices.swap(0, idx); + } + return Ok(Some(res)); + } + } + } + + for lodb in snapshot.loose_dbs.iter() { + // TODO: remove this double-lookup once the borrow checker allows it. + if lodb.contains(id) { + return lodb + .try_find(id, buffer) + .map(|obj| obj.map(|obj| (obj, None))) + .map_err(Into::into); + } + } + + match self.store.load_one_index(self.refresh, snapshot.marker)? { + Some(new_snapshot) => { + *snapshot = new_snapshot; + self.clear_cache(); + } + None => return Ok(None), + } + } + } + + pub(crate) fn clear_cache(&self) { + self.packed_object_count.borrow_mut().take(); + } +} + +impl<S> gix_pack::Find for super::Handle<S> +where + S: Deref<Target = super::Store> + Clone, +{ + type Error = Error; + + // TODO: probably make this method fallible, but that would mean its own error type. + fn contains(&self, id: impl AsRef<gix_hash::oid>) -> bool { + let id = id.as_ref(); + let mut snapshot = self.snapshot.borrow_mut(); + loop { + for (idx, index) in snapshot.indices.iter().enumerate() { + if index.contains(id) { + if idx != 0 { + snapshot.indices.swap(0, idx); + } + return true; + } + } + + for lodb in snapshot.loose_dbs.iter() { + if lodb.contains(id) { + return true; + } + } + + match self.store.load_one_index(self.refresh, snapshot.marker) { + Ok(Some(new_snapshot)) => { + *snapshot = new_snapshot; + self.clear_cache(); + } + Ok(None) => return false, // nothing more to load, or our refresh mode doesn't allow disk refreshes + Err(_) => return false, // something went wrong, nothing we can communicate here with this trait. TODO: Maybe that should change? + } + } + } + + fn try_find_cached<'a>( + &self, + id: impl AsRef<gix_hash::oid>, + buffer: &'a mut Vec<u8>, + pack_cache: &mut impl DecodeEntry, + ) -> Result<Option<(gix_object::Data<'a>, Option<gix_pack::data::entry::Location>)>, Self::Error> { + let id = id.as_ref(); + let mut snapshot = self.snapshot.borrow_mut(); + self.try_find_cached_inner(id, buffer, pack_cache, &mut snapshot, None) + } + + fn location_by_oid( + &self, + id: impl AsRef<gix_hash::oid>, + buf: &mut Vec<u8>, + ) -> Option<gix_pack::data::entry::Location> { + assert!( + matches!(self.token.as_ref(), Some(handle::Mode::KeepDeletedPacksAvailable)), + "BUG: handle must be configured to `prevent_pack_unload()` before using this method" + ); + + assert!(self.store_ref().replacements.is_empty() || self.ignore_replacements, "Everything related to packing must not use replacements. These are not used here, but it should be turned off for good measure."); + + let id = id.as_ref(); + let mut snapshot = self.snapshot.borrow_mut(); + 'outer: loop { + { + let marker = snapshot.marker; + for (idx, index) in snapshot.indices.iter_mut().enumerate() { + if let Some(handle::index_lookup::Outcome { + object_index: handle::IndexForObjectInPack { pack_id, pack_offset }, + index_file: _, + pack: possibly_pack, + }) = index.lookup(id) + { + let pack = match possibly_pack { + Some(pack) => pack, + None => match self.store.load_pack(pack_id, marker).ok()? { + Some(pack) => { + *possibly_pack = Some(pack); + possibly_pack.as_deref().expect("just put it in") + } + None => { + // The pack wasn't available anymore so we are supposed to try another round with a fresh index + match self.store.load_one_index(self.refresh, snapshot.marker).ok()? { + Some(new_snapshot) => { + *snapshot = new_snapshot; + self.clear_cache(); + continue 'outer; + } + None => { + // nothing new in the index, kind of unexpected to not have a pack but to also + // to have no new index yet. We set the new index before removing any slots, so + // this should be observable. + return None; + } + } + } + }, + }; + let entry = pack.entry(pack_offset); + + buf.resize(entry.decompressed_size.try_into().expect("representable size"), 0); + assert_eq!(pack.id, pack_id.to_intrinsic_pack_id(), "both ids must always match"); + + let res = pack.decompress_entry(&entry, buf).ok().map(|entry_size_past_header| { + gix_pack::data::entry::Location { + pack_id: pack.id, + pack_offset, + entry_size: entry.header_size() + entry_size_past_header, + } + }); + + if idx != 0 { + snapshot.indices.swap(0, idx); + } + return res; + } + } + } + + match self.store.load_one_index(self.refresh, snapshot.marker).ok()? { + Some(new_snapshot) => { + *snapshot = new_snapshot; + self.clear_cache(); + } + None => return None, + } + } + } + + fn pack_offsets_and_oid(&self, pack_id: u32) -> Option<Vec<(u64, gix_hash::ObjectId)>> { + assert!( + matches!(self.token.as_ref(), Some(handle::Mode::KeepDeletedPacksAvailable)), + "BUG: handle must be configured to `prevent_pack_unload()` before using this method" + ); + let pack_id = PackId::from_intrinsic_pack_id(pack_id); + loop { + let snapshot = self.snapshot.borrow(); + { + for index in snapshot.indices.iter() { + if let Some(iter) = index.iter(pack_id) { + return Some(iter.map(|e| (e.pack_offset, e.oid)).collect()); + } + } + } + + match self.store.load_one_index(self.refresh, snapshot.marker).ok()? { + Some(new_snapshot) => { + drop(snapshot); + *self.snapshot.borrow_mut() = new_snapshot; + } + None => return None, + } + } + } + + fn entry_by_location(&self, location: &gix_pack::data::entry::Location) -> Option<gix_pack::find::Entry> { + assert!( + matches!(self.token.as_ref(), Some(handle::Mode::KeepDeletedPacksAvailable)), + "BUG: handle must be configured to `prevent_pack_unload()` before using this method" + ); + let pack_id = PackId::from_intrinsic_pack_id(location.pack_id); + let mut snapshot = self.snapshot.borrow_mut(); + let marker = snapshot.marker; + loop { + { + for index in snapshot.indices.iter_mut() { + if let Some(possibly_pack) = index.pack(pack_id) { + let pack = match possibly_pack { + Some(pack) => pack, + None => { + let pack = self.store.load_pack(pack_id, marker).ok()?.expect( + "BUG: pack must exist from previous call to location_by_oid() and must not be unloaded", + ); + *possibly_pack = Some(pack); + possibly_pack.as_deref().expect("just put it in") + } + }; + return pack + .entry_slice(location.entry_range(location.pack_offset)) + .map(|data| gix_pack::find::Entry { + data: data.to_owned(), + version: pack.version(), + }); + } + } + } + + snapshot.indices.insert( + 0, + self.store + .index_by_id(pack_id, marker) + .expect("BUG: index must always be present, must not be unloaded or overwritten"), + ); + } + } +} + +impl<S> Find for super::Handle<S> +where + S: Deref<Target = super::Store> + Clone, + Self: gix_pack::Find, +{ + type Error = <Self as gix_pack::Find>::Error; + + fn contains(&self, id: impl AsRef<gix_hash::oid>) -> bool { + gix_pack::Find::contains(self, id) + } + + fn try_find<'a>( + &self, + id: impl AsRef<gix_hash::oid>, + buffer: &'a mut Vec<u8>, + ) -> Result<Option<gix_object::Data<'a>>, Self::Error> { + gix_pack::Find::try_find(self, id, buffer).map(|t| t.map(|t| t.0)) + } +} diff --git a/vendor/gix-odb/src/store_impls/dynamic/handle.rs b/vendor/gix-odb/src/store_impls/dynamic/handle.rs new file mode 100644 index 000000000..78efd4451 --- /dev/null +++ b/vendor/gix-odb/src/store_impls/dynamic/handle.rs @@ -0,0 +1,399 @@ +use std::{ + cell::RefCell, + convert::{TryFrom, TryInto}, + ops::Deref, + rc::Rc, + sync::{atomic::Ordering, Arc}, +}; + +use gix_features::threading::OwnShared; +use gix_hash::oid; + +use crate::store::{handle, types, RefreshMode}; + +pub(crate) enum SingleOrMultiIndex { + Single { + index: Arc<gix_pack::index::File>, + data: Option<Arc<gix_pack::data::File>>, + }, + Multi { + index: Arc<gix_pack::multi_index::File>, + data: Vec<Option<Arc<gix_pack::data::File>>>, + }, +} + +/// A utility to allow looking up pack offsets for a particular pack +pub(crate) enum IntraPackLookup<'a> { + Single(&'a gix_pack::index::File), + /// the internal pack-id inside of a multi-index for which the lookup is supposed to be. + /// Used to prevent ref-delta OIDs to, for some reason, point to a different pack. + Multi { + index: &'a gix_pack::multi_index::File, + required_pack_index: gix_pack::multi_index::PackIndex, + }, +} + +impl<'a> IntraPackLookup<'a> { + pub(crate) fn pack_offset_by_id(&self, id: &oid) -> Option<gix_pack::data::Offset> { + match self { + IntraPackLookup::Single(index) => index + .lookup(id) + .map(|entry_index| index.pack_offset_at_index(entry_index)), + IntraPackLookup::Multi { + index, + required_pack_index, + } => index.lookup(id).and_then(|entry_index| { + let (pack_index, pack_offset) = index.pack_id_and_pack_offset_at_index(entry_index); + (pack_index == *required_pack_index).then_some(pack_offset) + }), + } + } +} + +pub struct IndexLookup { + pub(crate) file: SingleOrMultiIndex, + /// The index we were found at in the slot map + pub(crate) id: types::IndexId, +} + +pub struct IndexForObjectInPack { + /// The internal identifier of the pack itself, which either is referred to by an index or a multi-pack index. + pub(crate) pack_id: types::PackId, + /// The offset at which the object's entry can be found + pub(crate) pack_offset: u64, +} + +pub(crate) mod index_lookup { + use std::{collections::HashSet, sync::Arc}; + + use gix_hash::oid; + + use crate::store::{handle, handle::IntraPackLookup, types}; + + pub(crate) struct Outcome<'a> { + pub object_index: handle::IndexForObjectInPack, + pub index_file: IntraPackLookup<'a>, + pub pack: &'a mut Option<Arc<gix_pack::data::File>>, + } + + impl handle::IndexLookup { + /// Return an iterator over the entries of the given pack. The `pack_id` is required to identify a pack uniquely within + /// a potential multi-pack index. + pub(crate) fn iter( + &self, + pack_id: types::PackId, + ) -> Option<Box<dyn Iterator<Item = gix_pack::index::Entry> + '_>> { + (self.id == pack_id.index).then(|| match &self.file { + handle::SingleOrMultiIndex::Single { index, .. } => index.iter(), + handle::SingleOrMultiIndex::Multi { index, .. } => { + let pack_index = pack_id.multipack_index.expect( + "BUG: multi-pack index must be set if this is a multi-pack, pack-indices seem unstable", + ); + Box::new(index.iter().filter_map(move |e| { + (e.pack_index == pack_index).then_some(gix_pack::index::Entry { + oid: e.oid, + pack_offset: e.pack_offset, + crc32: None, + }) + })) + } + }) + } + + pub(crate) fn pack(&mut self, pack_id: types::PackId) -> Option<&'_ mut Option<Arc<gix_pack::data::File>>> { + (self.id == pack_id.index).then(move || match &mut self.file { + handle::SingleOrMultiIndex::Single { data, .. } => data, + handle::SingleOrMultiIndex::Multi { data, .. } => { + let pack_index = pack_id.multipack_index.expect( + "BUG: multi-pack index must be set if this is a multi-pack, pack-indices seem unstable", + ); + &mut data[pack_index as usize] + } + }) + } + + /// Return true if the given object id exists in this index + pub(crate) fn contains(&self, object_id: &oid) -> bool { + match &self.file { + handle::SingleOrMultiIndex::Single { index, .. } => index.lookup(object_id).is_some(), + handle::SingleOrMultiIndex::Multi { index, .. } => index.lookup(object_id).is_some(), + } + } + + /// Return true if the given object id exists in this index + pub(crate) fn oid_at_index(&self, entry_index: u32) -> &gix_hash::oid { + match &self.file { + handle::SingleOrMultiIndex::Single { index, .. } => index.oid_at_index(entry_index), + handle::SingleOrMultiIndex::Multi { index, .. } => index.oid_at_index(entry_index), + } + } + + /// Return the amount of objects contained in the index, essentially the number of object ids. + pub(crate) fn num_objects(&self) -> u32 { + match &self.file { + handle::SingleOrMultiIndex::Single { index, .. } => index.num_objects(), + handle::SingleOrMultiIndex::Multi { index, .. } => index.num_objects(), + } + } + + /// Call `lookup_prefix(…)` on either index or multi-index, and transform matches into an object id. + pub(crate) fn lookup_prefix( + &self, + prefix: gix_hash::Prefix, + candidates: Option<&mut HashSet<gix_hash::ObjectId>>, + ) -> Option<crate::store::prefix::lookup::Outcome> { + let mut candidate_entries = candidates.as_ref().map(|_| 0..0); + let res = match &self.file { + handle::SingleOrMultiIndex::Single { index, .. } => { + index.lookup_prefix(prefix, candidate_entries.as_mut()) + } + handle::SingleOrMultiIndex::Multi { index, .. } => { + index.lookup_prefix(prefix, candidate_entries.as_mut()) + } + }?; + + if let Some((candidates, entries)) = candidates.zip(candidate_entries) { + candidates.extend(entries.map(|entry| self.oid_at_index(entry).to_owned())); + } + Some(res.map(|entry_index| self.oid_at_index(entry_index).to_owned())) + } + + /// See if the oid is contained in this index, and return its full id for lookup possibly alongside its data file if already + /// loaded. + /// Also return the index itself as it's needed to resolve intra-pack ref-delta objects. They are a possibility even though + /// they won't be used in practice as it's more efficient to store their offsets. + /// If it is not loaded, ask it to be loaded and put it into the returned mutable option for safe-keeping. + pub(crate) fn lookup(&mut self, object_id: &oid) -> Option<Outcome<'_>> { + let id = self.id; + match &mut self.file { + handle::SingleOrMultiIndex::Single { index, data } => index.lookup(object_id).map(move |idx| Outcome { + object_index: handle::IndexForObjectInPack { + pack_id: types::PackId { + index: id, + multipack_index: None, + }, + pack_offset: index.pack_offset_at_index(idx), + }, + index_file: IntraPackLookup::Single(index), + pack: data, + }), + handle::SingleOrMultiIndex::Multi { index, data } => index.lookup(object_id).map(move |idx| { + let (pack_index, pack_offset) = index.pack_id_and_pack_offset_at_index(idx); + Outcome { + object_index: handle::IndexForObjectInPack { + pack_id: types::PackId { + index: id, + multipack_index: Some(pack_index), + }, + pack_offset, + }, + index_file: IntraPackLookup::Multi { + index, + required_pack_index: pack_index, + }, + pack: &mut data[pack_index as usize], + } + }), + } + } + } +} + +pub(crate) enum Mode { + DeletedPacksAreInaccessible, + /// This mode signals that we should not unload packs even after they went missing. + KeepDeletedPacksAvailable, +} + +/// Handle registration +impl super::Store { + pub(crate) fn register_handle(&self) -> Mode { + self.num_handles_unstable.fetch_add(1, Ordering::Relaxed); + Mode::DeletedPacksAreInaccessible + } + pub(crate) fn remove_handle(&self, mode: Mode) { + match mode { + Mode::KeepDeletedPacksAvailable => { + let _lock = self.write.lock(); + self.num_handles_stable.fetch_sub(1, Ordering::SeqCst) + } + Mode::DeletedPacksAreInaccessible => self.num_handles_unstable.fetch_sub(1, Ordering::Relaxed), + }; + } + pub(crate) fn upgrade_handle(&self, mode: Mode) -> Mode { + if let Mode::DeletedPacksAreInaccessible = mode { + let _lock = self.write.lock(); + self.num_handles_stable.fetch_add(1, Ordering::SeqCst); + self.num_handles_unstable.fetch_sub(1, Ordering::SeqCst); + } + Mode::KeepDeletedPacksAvailable + } +} + +/// Handle creation +impl super::Store { + /// The amount of times a ref-delta base can be followed when multi-indices are involved. + pub const INITIAL_MAX_RECURSION_DEPTH: usize = 32; + + /// Create a new cache filled with a handle to this store, if this store is supporting shared ownership. + /// + /// Note that the actual type of `OwnShared` depends on the `parallel` feature toggle of the `gix-features` crate. + pub fn to_cache(self: &OwnShared<Self>) -> crate::Cache<super::Handle<OwnShared<super::Store>>> { + self.to_handle().into() + } + + /// Create a new cache filled with a handle to this store if this store is held in an `Arc`. + pub fn to_cache_arc(self: &Arc<Self>) -> crate::Cache<super::Handle<Arc<super::Store>>> { + self.to_handle_arc().into() + } + + /// Create a new database handle to this store if this store is supporting shared ownership. + /// + /// See also, [`to_cache()`][super::Store::to_cache()] which is probably more useful. + pub fn to_handle(self: &OwnShared<Self>) -> super::Handle<OwnShared<super::Store>> { + let token = self.register_handle(); + super::Handle { + store: self.clone(), + refresh: RefreshMode::default(), + ignore_replacements: false, + token: Some(token), + snapshot: RefCell::new(self.collect_snapshot()), + max_recursion_depth: Self::INITIAL_MAX_RECURSION_DEPTH, + packed_object_count: Default::default(), + } + } + + /// Create a new database handle to this store if this store is held in an `Arc`. + /// + /// This method is useful in applications that know they will use threads. + pub fn to_handle_arc(self: &Arc<Self>) -> super::Handle<Arc<super::Store>> { + let token = self.register_handle(); + super::Handle { + store: self.clone(), + refresh: Default::default(), + ignore_replacements: false, + token: Some(token), + snapshot: RefCell::new(self.collect_snapshot()), + max_recursion_depth: Self::INITIAL_MAX_RECURSION_DEPTH, + packed_object_count: Default::default(), + } + } + + /// Transform the only instance into an `Arc<Self>` or panic if this is not the only Rc handle + /// to the contained store. + /// + /// This is meant to be used when the `gix_features::threading::OwnShared` refers to an `Rc` as it was compiled without the + /// `parallel` feature toggle. + pub fn into_shared_arc(self: OwnShared<Self>) -> Arc<Self> { + match OwnShared::try_unwrap(self) { + Ok(this) => Arc::new(this), + Err(_) => panic!("BUG: Must be called when there is only one owner for this RC"), + } + } +} + +impl<S> super::Handle<S> +where + S: Deref<Target = super::Store> + Clone, +{ + /// Call once if pack ids are stored and later used for lookup, meaning they should always remain mapped and not be unloaded + /// even if they disappear from disk. + /// This must be called if there is a chance that git maintenance is happening while a pack is created. + pub fn prevent_pack_unload(&mut self) { + self.token = self.token.take().map(|token| self.store.upgrade_handle(token)); + } + + /// Return a shared reference to the contained store. + pub fn store_ref(&self) -> &S::Target { + &self.store + } + + /// Return an owned store with shared ownership. + pub fn store(&self) -> S { + self.store.clone() + } + + /// Set the handle to never cause ODB refreshes if an object could not be found. + /// + /// The latter is the default, as typically all objects referenced in a git repository are contained in the local clone. + /// More recently, however, this doesn't always have to be the case due to sparse checkouts and other ways to only have a + /// limited amount of objects available locally. + pub fn refresh_never(&mut self) { + self.refresh = RefreshMode::Never; + } + + /// Return the current refresh mode. + pub fn refresh_mode(&mut self) -> RefreshMode { + self.refresh + } +} + +impl<S> Drop for super::Handle<S> +where + S: Deref<Target = super::Store> + Clone, +{ + fn drop(&mut self) { + if let Some(token) = self.token.take() { + self.store.remove_handle(token) + } + } +} + +impl TryFrom<&super::Store> for super::Store { + type Error = std::io::Error; + + fn try_from(s: &super::Store) -> Result<Self, Self::Error> { + super::Store::at_opts( + s.path(), + s.replacements(), + crate::store::init::Options { + slots: crate::store::init::Slots::Given(s.files.len().try_into().expect("BUG: too many slots")), + object_hash: Default::default(), + use_multi_pack_index: false, + current_dir: s.current_dir.clone().into(), + }, + ) + } +} + +impl super::Handle<Rc<super::Store>> { + /// Convert a ref counted store into one that is ref-counted and thread-safe, by creating a new Store. + pub fn into_arc(self) -> std::io::Result<super::Handle<Arc<super::Store>>> { + let store = Arc::new(super::Store::try_from(self.store_ref())?); + let mut cache = store.to_handle_arc(); + cache.refresh = self.refresh; + cache.max_recursion_depth = self.max_recursion_depth; + Ok(cache) + } +} + +impl super::Handle<Arc<super::Store>> { + /// Convert a ref counted store into one that is ref-counted and thread-safe, by creating a new Store + pub fn into_arc(self) -> std::io::Result<super::Handle<Arc<super::Store>>> { + Ok(self) + } +} + +impl<S> Clone for super::Handle<S> +where + S: Deref<Target = super::Store> + Clone, +{ + fn clone(&self) -> Self { + super::Handle { + store: self.store.clone(), + refresh: self.refresh, + ignore_replacements: self.ignore_replacements, + token: { + let token = self.store.register_handle(); + match self.token.as_ref().expect("token is always set here ") { + handle::Mode::DeletedPacksAreInaccessible => token, + handle::Mode::KeepDeletedPacksAvailable => self.store.upgrade_handle(token), + } + .into() + }, + snapshot: RefCell::new(self.store.collect_snapshot()), + max_recursion_depth: self.max_recursion_depth, + packed_object_count: Default::default(), + } + } +} diff --git a/vendor/gix-odb/src/store_impls/dynamic/header.rs b/vendor/gix-odb/src/store_impls/dynamic/header.rs new file mode 100644 index 000000000..a1fb770ed --- /dev/null +++ b/vendor/gix-odb/src/store_impls/dynamic/header.rs @@ -0,0 +1,189 @@ +use std::ops::Deref; + +use gix_hash::oid; + +use super::find::Error; +use crate::{ + find::Header, + store::{find::error::DeltaBaseRecursion, handle, load_index}, +}; + +impl<S> super::Handle<S> +where + S: Deref<Target = super::Store> + Clone, +{ + fn try_header_inner<'b>( + &'b self, + mut id: &'b gix_hash::oid, + snapshot: &mut load_index::Snapshot, + recursion: Option<DeltaBaseRecursion<'_>>, + ) -> Result<Option<Header>, Error> { + if let Some(r) = recursion { + if r.depth >= self.max_recursion_depth { + return Err(Error::DeltaBaseRecursionLimit { + max_depth: self.max_recursion_depth, + id: r.original_id.to_owned(), + }); + } + } else if !self.ignore_replacements { + if let Ok(pos) = self + .store + .replacements + .binary_search_by(|(map_this, _)| map_this.as_ref().cmp(id)) + { + id = self.store.replacements[pos].1.as_ref(); + } + } + + 'outer: loop { + { + let marker = snapshot.marker; + for (idx, index) in snapshot.indices.iter_mut().enumerate() { + if let Some(handle::index_lookup::Outcome { + object_index: handle::IndexForObjectInPack { pack_id, pack_offset }, + index_file, + pack: possibly_pack, + }) = index.lookup(id) + { + let pack = match possibly_pack { + Some(pack) => pack, + None => match self.store.load_pack(pack_id, marker)? { + Some(pack) => { + *possibly_pack = Some(pack); + possibly_pack.as_deref().expect("just put it in") + } + None => { + // The pack wasn't available anymore so we are supposed to try another round with a fresh index + match self.store.load_one_index(self.refresh, snapshot.marker)? { + Some(new_snapshot) => { + *snapshot = new_snapshot; + self.clear_cache(); + continue 'outer; + } + None => { + // nothing new in the index, kind of unexpected to not have a pack but to also + // to have no new index yet. We set the new index before removing any slots, so + // this should be observable. + return Ok(None); + } + } + } + }, + }; + let entry = pack.entry(pack_offset); + let res = match pack.decode_header(entry, |id| { + index_file.pack_offset_by_id(id).map(|pack_offset| { + gix_pack::data::decode::header::ResolvedBase::InPack(pack.entry(pack_offset)) + }) + }) { + Ok(header) => Ok(header.into()), + Err(gix_pack::data::decode::Error::DeltaBaseUnresolved(base_id)) => { + // Only with multi-pack indices it's allowed to jump to refer to other packs within this + // multi-pack. Otherwise this would constitute a thin pack which is only allowed in transit. + // However, if we somehow end up with that, we will resolve it safely, even though we could + // avoid handling this case and error instead. + let hdr = self + .try_header_inner( + &base_id, + snapshot, + recursion + .map(|r| r.inc_depth()) + .or_else(|| DeltaBaseRecursion::new(id).into()), + ) + .map_err(|err| Error::DeltaBaseLookup { + err: Box::new(err), + base_id, + id: id.to_owned(), + })? + .ok_or_else(|| Error::DeltaBaseMissing { + base_id, + id: id.to_owned(), + })?; + let handle::index_lookup::Outcome { + object_index: + handle::IndexForObjectInPack { + pack_id: _, + pack_offset, + }, + index_file, + pack: possibly_pack, + } = match snapshot.indices[idx].lookup(id) { + Some(res) => res, + None => { + let mut out = None; + for index in snapshot.indices.iter_mut() { + out = index.lookup(id); + if out.is_some() { + break; + } + } + + out.unwrap_or_else(|| { + panic!("could not find object {id} in any index after looking up one of its base objects {base_id}") + }) + } + }; + let pack = possibly_pack + .as_ref() + .expect("pack to still be available like just now"); + let entry = pack.entry(pack_offset); + pack.decode_header(entry, |id| { + index_file + .pack_offset_by_id(id) + .map(|pack_offset| { + gix_pack::data::decode::header::ResolvedBase::InPack( + pack.entry(pack_offset), + ) + }) + .or_else(|| { + (id == base_id).then(|| { + gix_pack::data::decode::header::ResolvedBase::OutOfPack { + kind: hdr.kind(), + num_deltas: hdr.num_deltas(), + } + }) + }) + }) + .map(Into::into) + } + Err(err) => Err(err), + }?; + + if idx != 0 { + snapshot.indices.swap(0, idx); + } + return Ok(Some(res)); + } + } + } + + for lodb in snapshot.loose_dbs.iter() { + // TODO: remove this double-lookup once the borrow checker allows it. + if lodb.contains(id) { + return lodb.try_header(id).map(|opt| opt.map(Into::into)).map_err(Into::into); + } + } + + match self.store.load_one_index(self.refresh, snapshot.marker)? { + Some(new_snapshot) => { + *snapshot = new_snapshot; + self.clear_cache(); + } + None => return Ok(None), + } + } + } +} + +impl<S> crate::Header for super::Handle<S> +where + S: Deref<Target = super::Store> + Clone, +{ + type Error = Error; + + fn try_header(&self, id: impl AsRef<oid>) -> Result<Option<Header>, Self::Error> { + let id = id.as_ref(); + let mut snapshot = self.snapshot.borrow_mut(); + self.try_header_inner(id, &mut snapshot, None) + } +} diff --git a/vendor/gix-odb/src/store_impls/dynamic/init.rs b/vendor/gix-odb/src/store_impls/dynamic/init.rs new file mode 100644 index 000000000..2fb660ef1 --- /dev/null +++ b/vendor/gix-odb/src/store_impls/dynamic/init.rs @@ -0,0 +1,126 @@ +use std::{iter::FromIterator, path::PathBuf, sync::Arc}; + +use arc_swap::ArcSwap; + +use crate::{ + store::types::{MutableIndexAndPack, SlotMapIndex}, + Store, +}; + +/// Options for use in [`Store::at_opts()`]. +#[derive(Clone, Debug)] +pub struct Options { + /// How to obtain a size for the slot map. + pub slots: Slots, + /// The kind of hash we expect in our packs and would use for loose object iteration and object writing. + pub object_hash: gix_hash::Kind, + /// If false, no multi-pack indices will be used. If true, they will be used if their hash matches `object_hash`. + pub use_multi_pack_index: bool, + /// The current directory of the process at the time of instantiation. + /// If unset, it will be retrieved using `std::env::current_dir()`. + pub current_dir: Option<std::path::PathBuf>, +} + +impl Default for Options { + fn default() -> Self { + Options { + slots: Default::default(), + object_hash: Default::default(), + use_multi_pack_index: true, + current_dir: None, + } + } +} + +/// Configures the amount of slots in the index slotmap, which is fixed throughout the existence of the store. +#[derive(Copy, Clone, Debug)] +pub enum Slots { + /// The amount of slots to use, that is the total amount of indices we can hold at a time. + /// Using this has the advantage of avoiding an initial directory listing of the repository, and is recommended + /// on the server side where the repository setup is controlled. + /// + /// Note that this won't affect their packs, as each index can have one or more packs associated with it. + Given(u16), + /// Compute the amount of slots needed, as probably best used on the client side where a variety of repositories is encountered. + AsNeededByDiskState { + /// 1.0 means no safety, 1.1 means 10% more slots than needed + multiplier: f32, + /// The minimum amount of slots to assume + minimum: usize, + }, +} + +impl Default for Slots { + fn default() -> Self { + Slots::AsNeededByDiskState { + multiplier: 1.1, + minimum: 32, + } + } +} + +impl Store { + /// Open the store at `objects_dir` (containing loose objects and `packs/`), which must only be a directory for + /// the store to be created without any additional work being done. + /// `slots` defines how many multi-pack-indices as well as indices we can know about at a time, which includes + /// the allowance for all additional object databases coming in via `alternates` as well. + /// Note that the `slots` isn't used for packs, these are included with their multi-index or index respectively. + /// For example, In a repository with 250m objects and geometric packing one would expect 27 index/pack pairs, + /// or a single multi-pack index. + /// `replacements` is an iterator over pairs of old and new object ids for replacement support. + /// This means that when asking for object `X`, one will receive object `X-replaced` given an iterator like `Some((X, X-replaced))`. + pub fn at_opts( + objects_dir: impl Into<PathBuf>, + replacements: impl IntoIterator<Item = (gix_hash::ObjectId, gix_hash::ObjectId)>, + Options { + slots, + object_hash, + use_multi_pack_index, + current_dir, + }: Options, + ) -> std::io::Result<Self> { + let objects_dir = objects_dir.into(); + let current_dir = current_dir.map(Ok).unwrap_or_else(std::env::current_dir)?; + if !objects_dir.is_dir() { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, // TODO: use NotADirectory when stabilized + format!("'{}' wasn't a directory", objects_dir.display()), + )); + } + let slot_count = match slots { + Slots::Given(n) => n as usize, + Slots::AsNeededByDiskState { multiplier, minimum } => { + let mut db_paths = crate::alternate::resolve(&objects_dir, ¤t_dir) + .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?; + db_paths.insert(0, objects_dir.clone()); + let num_slots = super::Store::collect_indices_and_mtime_sorted_by_size(db_paths, None, None) + .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))? + .len(); + + ((num_slots as f32 * multiplier) as usize).max(minimum) + } + }; + if slot_count > crate::store::types::PackId::max_indices() { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "Cannot use more than 1^15 slots", + )); + } + let mut replacements: Vec<_> = replacements.into_iter().collect(); + replacements.sort_by(|a, b| a.0.cmp(&b.0)); + + Ok(Store { + current_dir, + write: Default::default(), + replacements, + path: objects_dir, + files: Vec::from_iter(std::iter::repeat_with(MutableIndexAndPack::default).take(slot_count)), + index: ArcSwap::new(Arc::new(SlotMapIndex::default())), + use_multi_pack_index, + object_hash, + num_handles_stable: Default::default(), + num_handles_unstable: Default::default(), + num_disk_state_consolidation: Default::default(), + }) + } +} diff --git a/vendor/gix-odb/src/store_impls/dynamic/iter.rs b/vendor/gix-odb/src/store_impls/dynamic/iter.rs new file mode 100644 index 000000000..bbe859e7c --- /dev/null +++ b/vendor/gix-odb/src/store_impls/dynamic/iter.rs @@ -0,0 +1,236 @@ +use std::{ops::Deref, option::Option::None, sync::Arc, vec::IntoIter}; + +use gix_hash::ObjectId; + +use crate::{ + loose, + store::{handle, handle::SingleOrMultiIndex, types::PackId}, + store_impls::dynamic, +}; + +struct EntryForOrdering { + pack_offset: u64, + entry_index: u32, + pack_index: u16, +} + +enum State { + Pack { + index_iter: IntoIter<handle::IndexLookup>, + index: handle::IndexLookup, + ordered_entries: Option<Vec<EntryForOrdering>>, + entry_index: u32, + num_objects: u32, + }, + Loose { + iter: loose::Iter, + index: usize, + }, + Depleted, +} + +/// Define the order in which objects are returned. +#[derive(Debug, Copy, Clone)] +pub enum Ordering { + /// Traverse packs first as sorted by their index files in lexicographical order (sorted by object id), then traverse loose objects + /// as sorted by their names as well. + /// + /// This mode uses no memory as it's the natural ordering of objects, and is best to obtain all object ids as quickly as possible, + /// while noting that these may contain duplicates. However, it's very costly to obtain object information or decode them with this + /// scheme as cache-hits are unlikely with it and memory maps are less efficient when loading them in random order. + PackLexicographicalThenLooseLexicographical, + /// Traverse packs first yielding object ids sorted by their position in the pack, with those at the beginning of the pack file coming first. + /// Then follow loose objects sorted by their names. + /// + /// This mode allocates and as to pre-sort objects by their offsets, delaying the start of the iteration once per pack while keeping + /// memory allocated once per pack. This price is usually worth paying once querying object information is planned as pack caches + /// are more efficiently used that way. + PackAscendingOffsetThenLooseLexicographical, +} + +impl Default for Ordering { + fn default() -> Self { + Ordering::PackLexicographicalThenLooseLexicographical + } +} + +/// An iterator over all, _possibly duplicate_, objects of an object store, which by default uses no extra memory but yields an +/// order that is costly to traverse when querying object information or decoding them. +/// +/// Use [`with_ordering()`][AllObjects::with_ordering()] to choose a performance trade-off. +pub struct AllObjects { + state: State, + num_objects: usize, + loose_dbs: Arc<Vec<loose::Store>>, + order: Ordering, +} + +/// Builder +impl AllObjects { + /// Set the ordering of the objects returned, trading off memory and latency for object query performance. + pub fn with_ordering(mut self, order: Ordering) -> Self { + self.order = order; + self + } +} + +impl AllObjects { + /// Create a new iterator from a dynamic store, which will be forced to load all indices eagerly and in the current thread. + pub fn new(db: &dynamic::Store) -> Result<Self, crate::store::load_index::Error> { + let snapshot = db.load_all_indices()?; + + let packed_objects = snapshot + .indices + .iter() + .fold(0usize, |dbc, index| dbc.saturating_add(index.num_objects() as usize)); + let mut index_iter = snapshot.indices.into_iter(); + let loose_dbs = snapshot.loose_dbs; + let order = Default::default(); + let state = match index_iter.next() { + Some(index) => { + let num_objects = index.num_objects(); + State::Pack { + index_iter, + ordered_entries: maybe_sort_entries(&index, order), + index, + entry_index: 0, + num_objects, + } + } + None => { + let index = 0; + State::Loose { + iter: loose_dbs.get(index).expect("at least one loose db").iter(), + index, + } + } + }; + Ok(AllObjects { + state, + loose_dbs, + num_objects: packed_objects, + order, + }) + } +} + +fn maybe_sort_entries(index: &handle::IndexLookup, order: Ordering) -> Option<Vec<EntryForOrdering>> { + let mut order: Vec<_> = match order { + Ordering::PackLexicographicalThenLooseLexicographical => return None, + Ordering::PackAscendingOffsetThenLooseLexicographical => match &index.file { + // We know that we cannot have more than u32 entry indices per pack. + SingleOrMultiIndex::Single { index, .. } => index + .iter() + .enumerate() + .map(|(idx, e)| EntryForOrdering { + pack_offset: e.pack_offset, + entry_index: idx as u32, + pack_index: 0, + }) + .collect(), + SingleOrMultiIndex::Multi { index, .. } => index + .iter() + .enumerate() + .map(|(idx, e)| EntryForOrdering { + pack_offset: e.pack_offset, + entry_index: idx as u32, + pack_index: { + debug_assert!( + e.pack_index < PackId::max_packs_in_multi_index(), + "this shows the relation between u16 and pack_index (u32) and why this is OK" + ); + e.pack_index as u16 + }, + }) + .collect(), + }, + }; + order.sort_by(|a, b| { + a.pack_index + .cmp(&b.pack_index) + .then_with(|| a.pack_offset.cmp(&b.pack_offset)) + }); + Some(order) +} + +impl Iterator for AllObjects { + type Item = Result<ObjectId, loose::iter::Error>; + + fn next(&mut self) -> Option<Self::Item> { + match &mut self.state { + State::Depleted => None, + State::Pack { + index_iter, + ordered_entries, + index, + entry_index, + num_objects, + } => { + if *entry_index < *num_objects { + let oid = match ordered_entries { + Some(entries) => index.oid_at_index(entries[*entry_index as usize].entry_index), + None => index.oid_at_index(*entry_index), + } + .to_owned(); + *entry_index += 1; + Some(Ok(oid)) + } else { + match index_iter.next() { + Some(new_index) => { + *ordered_entries = maybe_sort_entries(&new_index, self.order); + *index = new_index; + *entry_index = 0; + *num_objects = index.num_objects(); + } + None => { + let index = 0; + self.state = State::Loose { + iter: self.loose_dbs.get(index).expect("at least one loose odb").iter(), + index, + } + } + } + self.next() + } + } + State::Loose { iter, index } => match iter.next() { + Some(id) => Some(id), + None => { + *index += 1; + match self.loose_dbs.get(*index).map(|ldb| ldb.iter()) { + Some(new_iter) => { + *iter = new_iter; + self.next() + } + None => { + self.state = State::Depleted; + None + } + } + } + }, + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + (self.num_objects, None) + } +} + +impl<S> super::Handle<S> +where + S: Deref<Target = super::Store> + Clone, +{ + /// Return an iterator over all, _possibly duplicate_, objects, first the ones in all packs of all linked databases (via alternates), + /// followed by all loose objects. + pub fn iter(&self) -> Result<AllObjects, dynamic::load_index::Error> { + AllObjects::new(self.store_ref()) + } +} + +impl dynamic::Store { + /// Like [`Handle::iter()`][super::Handle::iter()], but accessible directly on the store. + pub fn iter(&self) -> Result<AllObjects, dynamic::load_index::Error> { + AllObjects::new(self) + } +} diff --git a/vendor/gix-odb/src/store_impls/dynamic/load_index.rs b/vendor/gix-odb/src/store_impls/dynamic/load_index.rs new file mode 100644 index 000000000..86cf6c43b --- /dev/null +++ b/vendor/gix-odb/src/store_impls/dynamic/load_index.rs @@ -0,0 +1,719 @@ +use std::{ + collections::{BTreeMap, VecDeque}, + ffi::OsStr, + ops::Deref, + path::{Path, PathBuf}, + sync::{ + atomic::{AtomicU16, AtomicUsize, Ordering}, + Arc, + }, + time::SystemTime, +}; + +use crate::store::{handle, types, RefreshMode}; + +pub(crate) struct Snapshot { + /// Indices ready for object lookup or contains checks, ordered usually by modification data, recent ones first. + pub(crate) indices: Vec<handle::IndexLookup>, + /// A set of loose objects dbs to search once packed objects weren't found. + pub(crate) loose_dbs: Arc<Vec<crate::loose::Store>>, + /// remember what this state represents and to compare to other states. + pub(crate) marker: types::SlotIndexMarker, +} + +mod error { + use std::path::PathBuf; + + use gix_pack::multi_index::PackIndex; + + /// Returned by [`crate::at_opts()`] + #[derive(thiserror::Error, Debug)] + #[allow(missing_docs)] + pub enum Error { + #[error("The objects directory at '{0}' is not an accessible directory")] + Inaccessible(PathBuf), + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + Alternate(#[from] crate::alternate::Error), + #[error("The slotmap turned out to be too small with {} entries, would need {} more", .current, .needed)] + InsufficientSlots { current: usize, needed: usize }, + /// The problem here is that some logic assumes that more recent generations are higher than previous ones. If we would overflow, + /// we would break that invariant which can lead to the wrong object from being returned. It would probably be super rare, but… + /// let's not risk it. + #[error( + "Would have overflown amount of max possible generations of {}", + super::Generation::MAX + )] + GenerationOverflow, + #[error("Cannot numerically handle more than {limit} packs in a single multi-pack index, got {actual} in file {index_path:?}")] + TooManyPacksInMultiIndex { + actual: PackIndex, + limit: PackIndex, + index_path: PathBuf, + }, + } +} + +pub use error::Error; + +use crate::store::types::{Generation, IndexAndPacks, MutableIndexAndPack, PackId, SlotMapIndex}; + +impl super::Store { + /// Load all indices, refreshing from disk only if needed. + pub(crate) fn load_all_indices(&self) -> Result<Snapshot, Error> { + let mut snapshot = self.collect_snapshot(); + while let Some(new_snapshot) = self.load_one_index(RefreshMode::Never, snapshot.marker)? { + snapshot = new_snapshot + } + Ok(snapshot) + } + + /// If `None` is returned, there is new indices and the caller should give up. This is a possibility even if it's allowed to refresh + /// as here might be no change to pick up. + pub(crate) fn load_one_index( + &self, + refresh_mode: RefreshMode, + marker: types::SlotIndexMarker, + ) -> Result<Option<Snapshot>, Error> { + let index = self.index.load(); + if !index.is_initialized() { + return self.consolidate_with_disk_state(true /* needs_init */, false /*load one new index*/); + } + + if marker.generation != index.generation || marker.state_id != index.state_id() { + // We have a more recent state already, provide it. + Ok(Some(self.collect_snapshot())) + } else { + // always compare to the latest state + // Nothing changed in the mean time, try to load another index… + if self.load_next_index(index) { + Ok(Some(self.collect_snapshot())) + } else { + // …and if that didn't yield anything new consider refreshing our disk state. + match refresh_mode { + RefreshMode::Never => Ok(None), + RefreshMode::AfterAllIndicesLoaded => { + self.consolidate_with_disk_state(false /* needs init */, true /*load one new index*/) + } + } + } + } + } + + /// load a new index (if not yet loaded), and return true if one was indeed loaded (leading to a state_id() change) of the current index. + /// Note that interacting with the slot-map is inherently racy and we have to deal with it, being conservative in what we even try to load + /// as our index might already be out-of-date as we try to use it to learn what's next. + fn load_next_index(&self, mut index: arc_swap::Guard<Arc<SlotMapIndex>>) -> bool { + 'retry_with_changed_index: loop { + let previous_state_id = index.state_id(); + 'retry_with_next_slot_index: loop { + match index + .next_index_to_load + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |current| { + (current != index.slot_indices.len()).then_some(current + 1) + }) { + Ok(slot_map_index) => { + // This slot-map index is in bounds and was only given to us. + let _ongoing_operation = IncOnNewAndDecOnDrop::new(&index.num_indices_currently_being_loaded); + let slot = &self.files[index.slot_indices[slot_map_index]]; + let _lock = slot.write.lock(); + if slot.generation.load(Ordering::SeqCst) > index.generation { + // There is a disk consolidation in progress which just overwrote a slot that cold be disposed with some other + // index, one we didn't intend to load. + // Continue with the next slot index in the hope there is something else we can do… + continue 'retry_with_next_slot_index; + } + let mut bundle = slot.files.load_full(); + let bundle_mut = Arc::make_mut(&mut bundle); + if let Some(files) = bundle_mut.as_mut() { + // these are always expected to be set, unless somebody raced us. We handle this later by retrying. + let _loaded_count = IncOnDrop(&index.loaded_indices); + match files.load_index(self.object_hash) { + Ok(_) => { + slot.files.store(bundle); + break 'retry_with_next_slot_index; + } + Err(_) => { + slot.files.store(bundle); + continue 'retry_with_next_slot_index; + } + } + } + } + Err(_nothing_more_to_load) => { + // There can be contention as many threads start working at the same time and take all the + // slots to load indices for. Some threads might just be left-over and have to wait for something + // to change. + let num_load_operations = index.num_indices_currently_being_loaded.deref(); + // TODO: potentially hot loop - could this be a condition variable? + while num_load_operations.load(Ordering::Relaxed) != 0 { + std::thread::yield_now() + } + break 'retry_with_next_slot_index; + } + } + } + if previous_state_id == index.state_id() { + let potentially_new_index = self.index.load(); + if Arc::as_ptr(&potentially_new_index) == Arc::as_ptr(&index) { + // There isn't a new index with which to retry the whole ordeal, so nothing could be done here. + return false; + } else { + // the index changed, worth trying again + index = potentially_new_index; + continue 'retry_with_changed_index; + } + } else { + // something inarguably changed, probably an index was loaded. 'probably' because we consider failed loads valid attempts, + // even they don't change anything for the caller which would then do a round for nothing. + return true; + } + } + } + + /// refresh and possibly clear out our existing data structures, causing all pack ids to be invalidated. + /// `load_new_index` is an optimization to at least provide one newly loaded pack after refreshing the slot map. + pub(crate) fn consolidate_with_disk_state( + &self, + needs_init: bool, + load_new_index: bool, + ) -> Result<Option<Snapshot>, Error> { + let index = self.index.load(); + let previous_index_state = Arc::as_ptr(&index) as usize; + + // IMPORTANT: get a lock after we recorded the previous state. + let write = self.write.lock(); + let objects_directory = &self.path; + + // Now we know the index isn't going to change anymore, even though threads might still load indices in the meantime. + let index = self.index.load(); + if previous_index_state != Arc::as_ptr(&index) as usize { + // Someone else took the look before and changed the index. Return it without doing any additional work. + return Ok(Some(self.collect_snapshot())); + } + + let was_uninitialized = !index.is_initialized(); + + // We might not be able to detect by pointer if the state changed, as this itself is racy. So we keep track of double-initialization + // using a flag, which means that if `needs_init` was true we saw the index uninitialized once, but now that we are here it's + // initialized meaning that somebody was faster and we couldn't detect it by comparisons to the index. + // If so, make sure we collect the snapshot instead of returning None in case nothing actually changed, which is likely with a + // race like this. + if !was_uninitialized && needs_init { + return Ok(Some(self.collect_snapshot())); + } + self.num_disk_state_consolidation.fetch_add(1, Ordering::Relaxed); + + let db_paths: Vec<_> = std::iter::once(objects_directory.to_owned()) + .chain(crate::alternate::resolve(objects_directory, &self.current_dir)?) + .collect(); + + // turn db paths into loose object databases. Reuse what's there, but only if it is in the right order. + let loose_dbs = if was_uninitialized + || db_paths.len() != index.loose_dbs.len() + || db_paths + .iter() + .zip(index.loose_dbs.iter().map(|ldb| &ldb.path)) + .any(|(lhs, rhs)| lhs != rhs) + { + Arc::new( + db_paths + .iter() + .map(|path| crate::loose::Store::at(path, self.object_hash)) + .collect::<Vec<_>>(), + ) + } else { + Arc::clone(&index.loose_dbs) + }; + + let indices_by_modification_time = Self::collect_indices_and_mtime_sorted_by_size( + db_paths, + index.slot_indices.len().into(), + self.use_multi_pack_index.then_some(self.object_hash), + )?; + let mut idx_by_index_path: BTreeMap<_, _> = index + .slot_indices + .iter() + .filter_map(|&idx| { + let f = &self.files[idx]; + Option::as_ref(&f.files.load()).map(|f| (f.index_path().to_owned(), idx)) + }) + .collect(); + + let mut new_slot_map_indices = Vec::new(); // these indices into the slot map still exist there/didn't change + let mut index_paths_to_add = was_uninitialized + .then(|| VecDeque::with_capacity(indices_by_modification_time.len())) + .unwrap_or_default(); + + // Figure out this number based on what we see while handling the existing indices + let mut num_loaded_indices = 0; + for (index_info, mtime) in indices_by_modification_time.into_iter().map(|(a, b, _)| (a, b)) { + match idx_by_index_path.remove(index_info.path()) { + Some(slot_idx) => { + let slot = &self.files[slot_idx]; + let files_guard = slot.files.load(); + let files = + Option::as_ref(&files_guard).expect("slot is set or we wouldn't know it points to this file"); + if index_info.is_multi_index() && files.mtime() != mtime { + // we have a changed multi-pack index. We can't just change the existing slot as it may alter slot indices + // that are currently available. Instead we have to move what's there into a new slot, along with the changes, + // and later free the slot or dispose of the index in the slot (like we do for removed/missing files). + index_paths_to_add.push_back((index_info, mtime, Some(slot_idx))); + // If the current slot is loaded, the soon-to-be copied multi-index path will be loaded as well. + if files.index_is_loaded() { + num_loaded_indices += 1; + } + } else { + // packs and indices are immutable, so no need to check modification times. Unchanged multi-pack indices also + // are handled like this just to be sure they are in the desired state. For these, the only way this could happen + // is if somebody deletes and then puts back + if Self::assure_slot_matches_index(&write, slot, index_info, mtime, index.generation) { + num_loaded_indices += 1; + } + new_slot_map_indices.push(slot_idx); + } + } + None => index_paths_to_add.push_back((index_info, mtime, None)), + } + } + let needs_stable_indices = self.maintain_stable_indices(&write); + + let mut next_possibly_free_index = index + .slot_indices + .iter() + .max() + .map(|idx| (idx + 1) % self.files.len()) + .unwrap_or(0); + let mut num_indices_checked = 0; + let mut needs_generation_change = false; + let mut slot_indices_to_remove: Vec<_> = idx_by_index_path.into_values().collect(); + while let Some((mut index_info, mtime, move_from_slot_idx)) = index_paths_to_add.pop_front() { + 'increment_slot_index: loop { + if num_indices_checked == self.files.len() { + return Err(Error::InsufficientSlots { + current: self.files.len(), + needed: index_paths_to_add.len() + 1, /*the one currently popped off*/ + }); + } + let slot_index = next_possibly_free_index; + let slot = &self.files[slot_index]; + next_possibly_free_index = (next_possibly_free_index + 1) % self.files.len(); + num_indices_checked += 1; + match move_from_slot_idx { + Some(move_from_slot_idx) => { + debug_assert!(index_info.is_multi_index(), "only set for multi-pack indices"); + if slot_index == move_from_slot_idx { + // don't try to move onto ourselves + continue 'increment_slot_index; + } + match Self::try_set_index_slot( + &write, + slot, + index_info, + mtime, + index.generation, + needs_stable_indices, + ) { + Ok(dest_was_empty) => { + slot_indices_to_remove.push(move_from_slot_idx); + new_slot_map_indices.push(slot_index); + // To avoid handling out the wrong pack (due to reassigned pack ids), declare this a new generation. + if !dest_was_empty { + needs_generation_change = true; + } + break 'increment_slot_index; + } + Err(unused_index_info) => index_info = unused_index_info, + } + } + None => { + match Self::try_set_index_slot( + &write, + slot, + index_info, + mtime, + index.generation, + needs_stable_indices, + ) { + Ok(dest_was_empty) => { + new_slot_map_indices.push(slot_index); + if !dest_was_empty { + needs_generation_change = true; + } + break 'increment_slot_index; + } + Err(unused_index_info) => index_info = unused_index_info, + } + } + } + // This isn't racy as it's only us who can change the Option::Some/None state of a slot. + } + } + assert_eq!( + index_paths_to_add.len(), + 0, + "By this time we have assigned all new files to slots" + ); + + let generation = if needs_generation_change { + index.generation.checked_add(1).ok_or(Error::GenerationOverflow)? + } else { + index.generation + }; + let index_unchanged = index.slot_indices == new_slot_map_indices; + if generation != index.generation { + assert!( + !index_unchanged, + "if the generation changed, the slot index must have changed for sure" + ); + } + if !index_unchanged || loose_dbs != index.loose_dbs { + let new_index = Arc::new(SlotMapIndex { + slot_indices: new_slot_map_indices, + loose_dbs, + generation, + // if there was a prior generation, some indices might already be loaded. But we deal with it by trying to load the next index then, + // until we find one. + next_index_to_load: index_unchanged + .then(|| Arc::clone(&index.next_index_to_load)) + .unwrap_or_default(), + loaded_indices: index_unchanged + .then(|| Arc::clone(&index.loaded_indices)) + .unwrap_or_else(|| Arc::new(num_loaded_indices.into())), + num_indices_currently_being_loaded: Default::default(), + }); + self.index.store(new_index); + } + + // deleted items - remove their slots AFTER we have set the new index if we may alter indices, otherwise we only declare them garbage. + // removing slots may cause pack loading to fail, and they will then reload their indices. + for slot in slot_indices_to_remove.into_iter().map(|idx| &self.files[idx]) { + let _lock = slot.write.lock(); + let mut files = slot.files.load_full(); + let files_mut = Arc::make_mut(&mut files); + if needs_stable_indices { + if let Some(files) = files_mut.as_mut() { + files.trash(); + // generation stays the same, as it's the same value still but scheduled for eventual removal. + } + } else { + *files_mut = None; + }; + slot.files.store(files); + if !needs_stable_indices { + // Not racy due to lock, generation must be set after unsetting the slot value AND storing it. + slot.generation.store(generation, Ordering::SeqCst); + } + } + + let new_index = self.index.load(); + Ok(if index.state_id() == new_index.state_id() { + // there was no change, and nothing was loaded in the meantime, reflect that in the return value to not get into loops + None + } else { + if load_new_index { + self.load_next_index(new_index); + } + Some(self.collect_snapshot()) + }) + } + + pub(crate) fn collect_indices_and_mtime_sorted_by_size( + db_paths: Vec<PathBuf>, + initial_capacity: Option<usize>, + multi_pack_index_object_hash: Option<gix_hash::Kind>, + ) -> Result<Vec<(Either, SystemTime, u64)>, Error> { + let mut indices_by_modification_time = Vec::with_capacity(initial_capacity.unwrap_or_default()); + for db_path in db_paths { + let packs = db_path.join("pack"); + let entries = match std::fs::read_dir(packs) { + Ok(e) => e, + Err(err) if err.kind() == std::io::ErrorKind::NotFound => continue, + Err(err) => return Err(err.into()), + }; + let indices = entries + .filter_map(Result::ok) + .filter_map(|e| e.metadata().map(|md| (e.path(), md)).ok()) + .filter(|(_, md)| md.file_type().is_file()) + .filter(|(p, _)| { + let ext = p.extension(); + (ext == Some(OsStr::new("idx")) && p.with_extension("pack").is_file()) + || (multi_pack_index_object_hash.is_some() && ext.is_none() && is_multipack_index(p)) + }) + .map(|(p, md)| md.modified().map_err(Error::from).map(|mtime| (p, mtime, md.len()))) + .collect::<Result<Vec<_>, _>>()?; + + let multi_index_info = multi_pack_index_object_hash + .and_then(|hash| { + indices.iter().find_map(|(p, a, b)| { + is_multipack_index(p) + .then(|| { + // we always open the multi-pack here to be able to remove indices + gix_pack::multi_index::File::at(p) + .ok() + .filter(|midx| midx.object_hash() == hash) + .map(|midx| (midx, *a, *b)) + }) + .flatten() + .map(|t| { + if t.0.num_indices() > PackId::max_packs_in_multi_index() { + Err(Error::TooManyPacksInMultiIndex { + index_path: p.to_owned(), + actual: t.0.num_indices(), + limit: PackId::max_packs_in_multi_index(), + }) + } else { + Ok(t) + } + }) + }) + }) + .transpose()?; + if let Some((multi_index, mtime, flen)) = multi_index_info { + let index_names_in_multi_index: Vec<_> = + multi_index.index_names().iter().map(|p| p.as_path()).collect(); + let mut indices_not_in_multi_index: Vec<(Either, _, _)> = indices + .into_iter() + .filter_map(|(path, a, b)| { + (path != multi_index.path() + && !index_names_in_multi_index + .contains(&Path::new(path.file_name().expect("file name present")))) + .then_some((Either::IndexPath(path), a, b)) + }) + .collect(); + indices_not_in_multi_index.insert(0, (Either::MultiIndexFile(Arc::new(multi_index)), mtime, flen)); + indices_by_modification_time.extend(indices_not_in_multi_index); + } else { + indices_by_modification_time.extend( + indices + .into_iter() + .filter_map(|(p, a, b)| (!is_multipack_index(&p)).then_some((Either::IndexPath(p), a, b))), + ) + } + } + // Unlike libgit2, do not sort by modification date, but by size and put the biggest indices first. That way + // the chance to hit an object should be higher. We leave it to the handle to sort by LRU. + // Git itself doesn't change the order which may safe time, but we want it to be stable which also helps some tests. + indices_by_modification_time.sort_by(|l, r| l.2.cmp(&r.2).reverse()); + Ok(indices_by_modification_time) + } + + /// returns Ok<dest slot was empty> if the copy could happen because dest-slot was actually free or disposable , and Some(true) if it was empty + #[allow(clippy::too_many_arguments)] + fn try_set_index_slot( + lock: &parking_lot::MutexGuard<'_, ()>, + dest_slot: &MutableIndexAndPack, + index_info: Either, + mtime: SystemTime, + current_generation: Generation, + needs_stable_indices: bool, + ) -> Result<bool, Either> { + let (dest_slot_was_empty, generation) = match &**dest_slot.files.load() { + Some(bundle) => { + if bundle.index_path() == index_info.path() || (bundle.is_disposable() && needs_stable_indices) { + // it might be possible to see ourselves in case all slots are taken, but there are still a few more destination + // slots to look for. + return Err(index_info); + } + // Since we overwrite an existing slot, we have to increment the generation to prevent anyone from trying to use it while + // before we are replacing it with a different value. + // In detail: + // We need to declare this to be the future to avoid anything in that slot to be returned to people who + // last saw the old state. They will then try to get a new index which by that time, might be happening + // in time so they get the latest one. If not, they will probably get into the same situation again until + // it finally succeeds. Alternatively, the object will be reported unobtainable, but at least it won't return + // some other object. + (false, current_generation + 1) + } + None => { + // For multi-pack indices: + // Do NOT copy the packs over, they need to be reopened to get the correct pack id matching the new slot map index. + // If we are allowed to delete the original, and nobody has the pack referenced, it is closed which is preferred. + // Thus we simply always start new with packs in multi-pack indices. + // In the worst case this could mean duplicate file handle usage though as the old and the new index can't share + // packs due to the intrinsic id. + // Note that the ID is used for cache access, too, so it must be unique. It must also be mappable from pack-id to slotmap id. + (true, current_generation) + } + }; + Self::set_slot_to_index(lock, dest_slot, index_info, mtime, generation); + Ok(dest_slot_was_empty) + } + + fn set_slot_to_index( + _lock: &parking_lot::MutexGuard<'_, ()>, + slot: &MutableIndexAndPack, + index_info: Either, + mtime: SystemTime, + generation: Generation, + ) { + let _lock = slot.write.lock(); + let mut files = slot.files.load_full(); + let files_mut = Arc::make_mut(&mut files); + // set the generation before we actually change the value, otherwise readers of old generations could observe the new one. + // We rather want them to turn around here and update their index, which, by that time, might actually already be available. + // If not, they would fail unable to load a pack or index they need, but that's preferred over returning wrong objects. + // Safety: can't race as we hold the lock, have to set the generation beforehand to help avoid others to observe the value. + slot.generation.store(generation, Ordering::SeqCst); + *files_mut = Some(index_info.into_index_and_packs(mtime)); + slot.files.store(files); + } + + /// Returns true if the index was left in a loaded state. + fn assure_slot_matches_index( + _lock: &parking_lot::MutexGuard<'_, ()>, + slot: &MutableIndexAndPack, + index_info: Either, + mtime: SystemTime, + current_generation: Generation, + ) -> bool { + match Option::as_ref(&slot.files.load()) { + Some(bundle) => { + assert_eq!( + bundle.index_path(), + index_info.path(), + "Parallel writers cannot change the file the slot points to." + ); + if bundle.is_disposable() { + // put it into the correct mode, it's now available for sure so should not be missing or garbage. + // The latter can happen if files are removed and put back for some reason, but we should definitely + // have them in a decent state now that we know/think they are there. + let _lock = slot.write.lock(); + let mut files = slot.files.load_full(); + let files_mut = Arc::make_mut(&mut files) + .as_mut() + .expect("BUG: cannot change from something to nothing, would be race"); + files_mut.put_back(); + debug_assert_eq!( + files_mut.mtime(), + mtime, + "BUG: we can only put back files that didn't obviously change" + ); + // Safety: can't race as we hold the lock, must be set before replacing the data. + // NOTE that we don't change the generation as it's still the very same index we talk about, it doesn't change + // identity. + slot.generation.store(current_generation, Ordering::SeqCst); + slot.files.store(files); + } else { + // it's already in the correct state, either loaded or unloaded. + } + bundle.index_is_loaded() + } + None => { + unreachable!("BUG: a slot can never be deleted if we have it recorded in the index WHILE changing said index. There shouldn't be a race") + } + } + } + + /// Stability means that indices returned by this API will remain valid. + /// Without that constraint, we may unload unused packs and indices, and may rebuild the slotmap index. + /// + /// Note that this must be called with a lock to the relevant state held to assure these values don't change while + /// we are working on said index. + fn maintain_stable_indices(&self, _guard: &parking_lot::MutexGuard<'_, ()>) -> bool { + self.num_handles_stable.load(Ordering::SeqCst) > 0 + } + + pub(crate) fn collect_snapshot(&self) -> Snapshot { + let index = self.index.load(); + let indices = if index.is_initialized() { + index + .slot_indices + .iter() + .map(|idx| (*idx, &self.files[*idx])) + .filter_map(|(id, file)| { + let lookup = match (**file.files.load()).as_ref()? { + types::IndexAndPacks::Index(bundle) => handle::SingleOrMultiIndex::Single { + index: bundle.index.loaded()?.clone(), + data: bundle.data.loaded().cloned(), + }, + types::IndexAndPacks::MultiIndex(multi) => handle::SingleOrMultiIndex::Multi { + index: multi.multi_index.loaded()?.clone(), + data: multi.data.iter().map(|f| f.loaded().cloned()).collect(), + }, + }; + handle::IndexLookup { file: lookup, id }.into() + }) + .collect() + } else { + Vec::new() + }; + + Snapshot { + indices, + loose_dbs: Arc::clone(&index.loose_dbs), + marker: index.marker(), + } + } +} + +// Outside of this method we will never assign new slot indices. +fn is_multipack_index(path: &Path) -> bool { + path.file_name() == Some(OsStr::new("multi-pack-index")) +} + +struct IncOnNewAndDecOnDrop<'a>(&'a AtomicU16); +impl<'a> IncOnNewAndDecOnDrop<'a> { + pub fn new(v: &'a AtomicU16) -> Self { + v.fetch_add(1, Ordering::SeqCst); + Self(v) + } +} +impl<'a> Drop for IncOnNewAndDecOnDrop<'a> { + fn drop(&mut self) { + self.0.fetch_sub(1, Ordering::SeqCst); + } +} + +struct IncOnDrop<'a>(&'a AtomicUsize); +impl<'a> Drop for IncOnDrop<'a> { + fn drop(&mut self) { + self.0.fetch_add(1, Ordering::SeqCst); + } +} + +pub(crate) enum Either { + IndexPath(PathBuf), + MultiIndexFile(Arc<gix_pack::multi_index::File>), +} + +impl Either { + fn path(&self) -> &Path { + match self { + Either::IndexPath(p) => p, + Either::MultiIndexFile(f) => f.path(), + } + } + + fn into_index_and_packs(self, mtime: SystemTime) -> IndexAndPacks { + match self { + Either::IndexPath(path) => IndexAndPacks::new_single(path, mtime), + Either::MultiIndexFile(file) => IndexAndPacks::new_multi_from_open_file(file, mtime), + } + } + + fn is_multi_index(&self) -> bool { + matches!(self, Either::MultiIndexFile(_)) + } +} + +impl Eq for Either {} + +impl PartialEq<Self> for Either { + fn eq(&self, other: &Self) -> bool { + self.path().eq(other.path()) + } +} + +impl PartialOrd<Self> for Either { + fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { + self.path().partial_cmp(other.path()) + } +} + +impl Ord for Either { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.path().cmp(other.path()) + } +} diff --git a/vendor/gix-odb/src/store_impls/dynamic/load_one.rs b/vendor/gix-odb/src/store_impls/dynamic/load_one.rs new file mode 100644 index 000000000..9961532c6 --- /dev/null +++ b/vendor/gix-odb/src/store_impls/dynamic/load_one.rs @@ -0,0 +1,154 @@ +use std::{ + path::Path, + sync::{atomic::Ordering, Arc}, +}; + +use crate::store::{handle, types}; + +impl super::Store { + /// If Ok(None) is returned, the pack-id was stale and referred to an unloaded pack or a pack which couldn't be + /// loaded as its file didn't exist on disk anymore. + /// If the oid is known, just load indices again to continue + /// (objects rarely ever removed so should be present, maybe in another pack though), + /// and redo the entire lookup for a valid pack id whose pack can probably be loaded next time. + pub(crate) fn load_pack( + &self, + id: types::PackId, + marker: types::SlotIndexMarker, + ) -> std::io::Result<Option<Arc<gix_pack::data::File>>> { + let index = self.index.load(); + if index.generation != marker.generation { + return Ok(None); + } + fn load_pack( + path: &Path, + id: types::PackId, + object_hash: gix_hash::Kind, + ) -> std::io::Result<Arc<gix_pack::data::File>> { + gix_pack::data::File::at(path, object_hash) + .map(|mut pack| { + pack.id = id.to_intrinsic_pack_id(); + Arc::new(pack) + }) + .map_err(|err| match err { + gix_pack::data::header::decode::Error::Io { source, .. } => source, + other => std::io::Error::new(std::io::ErrorKind::Other, other), + }) + } + + let slot = &self.files[id.index]; + // pin the current state before loading in the generation. That way we won't risk seeing the wrong value later. + let slot_files = &**slot.files.load(); + if slot.generation.load(Ordering::SeqCst) > marker.generation { + // There is a disk consolidation in progress which just overwrote a slot that could be disposed with some other + // pack, one we didn't intend to load. + // Hope that when the caller returns/retries the new index is set so they can fetch it and retry. + return Ok(None); + } + match id.multipack_index { + None => { + match slot_files { + Some(types::IndexAndPacks::Index(bundle)) => { + match bundle.data.loaded() { + Some(pack) => Ok(Some(pack.clone())), + None => { + let _lock = slot.write.lock(); + let mut files = slot.files.load_full(); + let files_mut = Arc::make_mut(&mut files); + let pack = match files_mut { + Some(types::IndexAndPacks::Index(bundle)) => bundle + .data + .load_with_recovery(|path| load_pack(path, id, self.object_hash))?, + Some(types::IndexAndPacks::MultiIndex(_)) => { + // something changed between us getting the lock, trigger a complete index refresh. + None + } + None => { + unreachable!("BUG: must set this handle to be stable to avoid slots to be cleared/changed") + } + }; + slot.files.store(files); + Ok(pack) + } + } + } + // This can also happen if they use an old index into our new and refreshed data which might have a multi-index + // here. + Some(types::IndexAndPacks::MultiIndex(_)) => Ok(None), + None => { + unreachable!("BUG: must set this handle to be stable to avoid slots to be cleared/changed") + } + } + } + Some(pack_index) => { + match slot_files { + Some(types::IndexAndPacks::MultiIndex(bundle)) => { + match bundle.data.get(pack_index as usize) { + None => Ok(None), // somewhat unexpected, data must be stale + Some(on_disk_pack) => match on_disk_pack.loaded() { + Some(pack) => Ok(Some(pack.clone())), + None => { + let _lock = slot.write.lock(); + let mut files = slot.files.load_full(); + let files_mut = Arc::make_mut(&mut files); + let pack = match files_mut { + Some(types::IndexAndPacks::Index(_)) => { + // something changed between us getting the lock, trigger a complete index refresh. + None + } + Some(types::IndexAndPacks::MultiIndex(bundle)) => bundle + .data + .get_mut(pack_index as usize) + .expect("BUG: must set this handle to be stable") + .load_with_recovery(|path| load_pack(path, id, self.object_hash))?, + None => { + unreachable!("BUG: must set this handle to be stable to avoid slots to be cleared/changed") + } + }; + slot.files.store(files); + Ok(pack) + } + }, + } + } + // This can also happen if they use an old index into our new and refreshed data which might have a multi-index + // here. + Some(types::IndexAndPacks::Index(_)) => Ok(None), + None => { + unreachable!("BUG: must set this handle to be stable to avoid slots to be cleared/changed") + } + } + } + } + } + + /// Similar to `.load_pack()`, but for entire indices, bypassing the index entirely and going solely by marker and id. + /// Returns `None` if the index wasn't available anymore or could otherwise not be loaded, which can be considered a bug + /// as we should always keep needed indices available. + pub(crate) fn index_by_id(&self, id: types::PackId, marker: types::SlotIndexMarker) -> Option<handle::IndexLookup> { + let slot = self.files.get(id.index)?; + // Pin this value before we check the generation to avoid seeing something newer later. + let slot_files = &**slot.files.load(); + if slot.generation.load(Ordering::SeqCst) > marker.generation { + // This means somebody just overwrote our trashed slot with a new (or about to be stored) index, which means the slot isn't + // what we need it to be. + // This shouldn't as we won't overwrite slots while handles need stable indices. + return None; + } + let lookup = match (slot_files).as_ref()? { + types::IndexAndPacks::Index(bundle) => handle::SingleOrMultiIndex::Single { + index: bundle.index.loaded()?.clone(), + data: bundle.data.loaded().cloned(), + }, + types::IndexAndPacks::MultiIndex(multi) => handle::SingleOrMultiIndex::Multi { + index: multi.multi_index.loaded()?.clone(), + data: multi.data.iter().map(|f| f.loaded().cloned()).collect(), + }, + }; + handle::IndexLookup { + id: id.index, + file: lookup, + } + .into() + } +} diff --git a/vendor/gix-odb/src/store_impls/dynamic/metrics.rs b/vendor/gix-odb/src/store_impls/dynamic/metrics.rs new file mode 100644 index 000000000..630674940 --- /dev/null +++ b/vendor/gix-odb/src/store_impls/dynamic/metrics.rs @@ -0,0 +1,80 @@ +use std::sync::atomic::Ordering; + +use crate::store::{types, types::IndexAndPacks}; + +impl super::Store { + /// Return metrics collected in a racy fashion, giving an idea of what's currently going on in the store. + /// + /// Use this to decide whether a new instance should be created to get a chance at dropping all open handles. + pub fn metrics(&self) -> types::Metrics { + let mut open_packs = 0; + let mut open_indices = 0; + let mut known_packs = 0; + let mut known_indices = 0; + let mut unused_slots = 0; + let mut unreachable_indices = 0; + let mut unreachable_packs = 0; + + let index = self.index.load(); + for f in index.slot_indices.iter().map(|idx| &self.files[*idx]) { + match &**f.files.load() { + Some(IndexAndPacks::Index(bundle)) => { + if bundle.index.is_loaded() { + open_indices += 1; + } + known_indices += 1; + if bundle.data.is_loaded() { + open_packs += 1; + } + known_packs += 1; + } + Some(IndexAndPacks::MultiIndex(multi)) => { + if multi.multi_index.is_loaded() { + open_indices += 1; + } + known_indices += 1; + for pack in multi.data.iter() { + if pack.is_loaded() { + open_packs += 1; + } + known_packs += 1; + } + } + None => {} + } + } + + for slot in &self.files { + match slot.files.load().as_ref() { + None => { + unused_slots += 1; + } + Some(bundle) => { + if bundle.is_disposable() { + unreachable_indices += 1; + unreachable_packs += match bundle { + IndexAndPacks::Index(single) => usize::from(single.data.is_loaded()), + IndexAndPacks::MultiIndex(multi) => { + multi.data.iter().map(|p| usize::from(p.is_loaded())).sum() + } + } + } + } + } + } + + types::Metrics { + num_handles: self.num_handles_unstable.load(Ordering::Relaxed) + + self.num_handles_stable.load(Ordering::Relaxed), + num_refreshes: self.num_disk_state_consolidation.load(Ordering::Relaxed), + open_reachable_packs: open_packs, + open_reachable_indices: open_indices, + known_reachable_indices: known_indices, + known_packs, + unused_slots, + loose_dbs: index.loose_dbs.len(), + unreachable_indices, + unreachable_packs, + } + } +} diff --git a/vendor/gix-odb/src/store_impls/dynamic/mod.rs b/vendor/gix-odb/src/store_impls/dynamic/mod.rs new file mode 100644 index 000000000..5cf2a26ee --- /dev/null +++ b/vendor/gix-odb/src/store_impls/dynamic/mod.rs @@ -0,0 +1,188 @@ +//! The standard object store which should fit all needs. +use std::{cell::RefCell, ops::Deref}; + +use crate::Store; + +/// This effectively acts like a handle but exists to be usable from the actual `crate::Handle` implementation which adds caches on top. +/// Each store is quickly cloned and contains thread-local state for shared packs. +pub struct Handle<S> +where + S: Deref<Target = Store> + Clone, +{ + pub(crate) store: S, + /// Defines what happens when there is no more indices to load. + pub refresh: RefreshMode, + /// The maximum recursion depth for resolving ref-delta base objects, that is objects referring to other objects within + /// a pack. + /// Recursive loops are possible only in purposefully crafted packs. + /// This value doesn't have to be huge as in typical scenarios, these kind of objects are rare and chains supposedly are + /// even more rare. + pub max_recursion_depth: usize, + + /// If true, replacements will not be performed even if these are available. + pub ignore_replacements: bool, + + pub(crate) token: Option<handle::Mode>, + snapshot: RefCell<load_index::Snapshot>, + packed_object_count: RefCell<Option<u64>>, +} + +/// Decide what happens when all indices are loaded. +#[derive(Clone, Copy)] +pub enum RefreshMode { + /// Check for new or changed pack indices (and pack data files) when the last known index is loaded. + /// During runtime we will keep pack indices stable by never reusing them, however, there is the option for + /// clearing internal caches which is likely to change pack ids and it will trigger unloading of packs as they are missing on disk. + AfterAllIndicesLoaded, + /// Use this if you expect a lot of missing objects that shouldn't trigger refreshes even after all packs are loaded. + /// This comes at the risk of not learning that the packs have changed in the mean time. + Never, +} + +impl Default for RefreshMode { + fn default() -> Self { + RefreshMode::AfterAllIndicesLoaded + } +} + +impl RefreshMode { + /// Set this refresh mode to never refresh. + pub fn never(&mut self) { + *self = RefreshMode::Never; + } +} + +/// +pub mod find; + +/// +pub mod prefix; + +mod header; + +/// +pub mod iter; + +/// +pub mod write; + +/// +pub mod init; + +pub(crate) mod types; +pub use types::Metrics; + +pub(crate) mod handle; + +/// +pub mod load_index; + +/// +pub mod verify; + +mod load_one; + +mod metrics; + +mod access; + +/// +pub mod structure { + use std::path::PathBuf; + + use crate::{store::load_index, types::IndexAndPacks, Store}; + + /// A record of a structural element of an object database. + #[derive(Debug, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] + pub enum Record { + /// A loose object database. + LooseObjectDatabase { + /// The root of the object database. + objects_directory: PathBuf, + /// The amount of object files. + num_objects: usize, + }, + /// A pack index file + Index { + /// The location of the index file, + path: PathBuf, + /// Whether or not the index is mapped into memory. + state: IndexState, + }, + /// A multi-index file + MultiIndex { + /// The location of the multi-index file, + path: PathBuf, + /// Whether or not the index is mapped into memory. + state: IndexState, + }, + /// An empty slot was encountered, this is possibly happening as the ODB changes during query with + /// a file being removed. + Empty, + } + + #[derive(Debug, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] + /// Possible stats of pack indices. + pub enum IndexState { + /// The index is active in memory because a mapping exists. + Loaded, + /// The index couldn't be unloaded as it was still in use, but that can happen another time. + Disposable, + /// The index isn't loaded/memory mapped. + Unloaded, + } + + impl Store { + /// Return information about all files known to us as well as their loading state. + /// + /// Note that this call is expensive as it gathers additional information about loose object databases. + /// Note that it may change as we collect information due to the highly volatile nature of the + /// implementation. The likelihood of actual changes is low though as these still depend on something + /// changing on disk and somebody reading at the same time. + pub fn structure(&self) -> Result<Vec<Record>, load_index::Error> { + let index = self.index.load(); + if !index.is_initialized() { + self.consolidate_with_disk_state(true, false /*load one new index*/)?; + } + let index = self.index.load(); + let mut res: Vec<_> = index + .loose_dbs + .iter() + .map(|db| Record::LooseObjectDatabase { + objects_directory: db.path.clone(), + num_objects: db.iter().count(), + }) + .collect(); + + for slot in index.slot_indices.iter().map(|idx| &self.files[*idx]) { + let files = slot.files.load(); + let record = match &**files { + Some(index) => { + let state = if index.is_disposable() { + IndexState::Disposable + } else if index.index_is_loaded() { + IndexState::Loaded + } else { + IndexState::Unloaded + }; + match index { + IndexAndPacks::Index(b) => Record::Index { + path: b.index.path().into(), + state, + }, + IndexAndPacks::MultiIndex(b) => Record::MultiIndex { + path: b.multi_index.path().into(), + state, + }, + } + } + None => Record::Empty, + }; + res.push(record); + } + Ok(res) + } + } +} diff --git a/vendor/gix-odb/src/store_impls/dynamic/prefix.rs b/vendor/gix-odb/src/store_impls/dynamic/prefix.rs new file mode 100644 index 000000000..9097c8cf6 --- /dev/null +++ b/vendor/gix-odb/src/store_impls/dynamic/prefix.rs @@ -0,0 +1,196 @@ +use std::{collections::HashSet, ops::Deref}; + +use crate::{ + store::{load_index, Handle}, + Find, +}; + +/// +pub mod lookup { + use crate::loose; + + /// Returned by [`Handle::lookup_prefix()`][crate::store::Handle::lookup_prefix()] + #[derive(thiserror::Error, Debug)] + #[allow(missing_docs)] + pub enum Error { + #[error("An error occurred looking up a prefix which requires iteration")] + LooseWalkDir(#[from] loose::iter::Error), + #[error(transparent)] + LoadIndex(#[from] crate::store::load_index::Error), + } + + /// A way to indicate if a lookup, despite successful, was ambiguous or yielded exactly + /// one result in the particular index. + pub type Outcome = Result<gix_hash::ObjectId, ()>; +} + +/// +pub mod disambiguate { + /// A potentially ambiguous prefix for use with `Handle::disambiguate_prefix()`. + #[derive(Debug, Copy, Clone)] + pub struct Candidate { + id: gix_hash::ObjectId, + hex_len: usize, + } + + impl Candidate { + /// Create a new potentially ambiguous prefix from an `id` and the desired minimal `hex_len`. + /// + /// It is considered ambiguous until it's disambiguated by validating that there is only a single object + /// matching this prefix. + pub fn new(id: impl Into<gix_hash::ObjectId>, hex_len: usize) -> Result<Self, gix_hash::prefix::Error> { + let id = id.into(); + gix_hash::Prefix::new(id, hex_len)?; + Ok(Candidate { id, hex_len }) + } + + /// Transform ourselves into a `Prefix` with our current hex lengths. + pub fn to_prefix(&self) -> gix_hash::Prefix { + gix_hash::Prefix::new(self.id, self.hex_len).expect("our hex-len to always be in bounds") + } + + pub(crate) fn inc_hex_len(&mut self) { + self.hex_len += 1; + assert!(self.hex_len <= self.id.kind().len_in_hex()); + } + + pub(crate) fn id(&self) -> &gix_hash::oid { + &self.id + } + + pub(crate) fn hex_len(&self) -> usize { + self.hex_len + } + } + + /// Returned by [`Handle::disambiguate_prefix()`][crate::store::Handle::disambiguate_prefix()] + #[derive(thiserror::Error, Debug)] + #[allow(missing_docs)] + pub enum Error { + #[error("An error occurred while trying to determine if a full hash contained in the object database")] + Contains(#[from] crate::store::find::Error), + #[error(transparent)] + Lookup(#[from] super::lookup::Error), + } +} + +impl<S> Handle<S> +where + S: Deref<Target = super::Store> + Clone, +{ + /// Return the exact number of packed objects after loading all currently available indices + /// as last seen on disk. + pub fn packed_object_count(&self) -> Result<u64, load_index::Error> { + let mut count = self.packed_object_count.borrow_mut(); + match *count { + Some(count) => Ok(count), + None => { + let mut snapshot = self.snapshot.borrow_mut(); + *snapshot = self.store.load_all_indices()?; + let mut obj_count = 0; + for index in &snapshot.indices { + obj_count += index.num_objects() as u64; + } + *count = Some(obj_count); + Ok(obj_count) + } + } + } + + /// Given a prefix `candidate` with an object id and an initial `hex_len`, check if it only matches a single + /// object within the entire object database and increment its `hex_len` by one until it is unambiguous. + /// Return `Ok(None)` if no object with that prefix exists. + pub fn disambiguate_prefix( + &self, + mut candidate: disambiguate::Candidate, + ) -> Result<Option<gix_hash::Prefix>, disambiguate::Error> { + let max_hex_len = candidate.id().kind().len_in_hex(); + if candidate.hex_len() == max_hex_len { + return Ok(self.contains(candidate.id()).then(|| candidate.to_prefix())); + } + + while candidate.hex_len() != max_hex_len { + let res = self.lookup_prefix(candidate.to_prefix(), None)?; + match res { + Some(Ok(_id)) => return Ok(Some(candidate.to_prefix())), + Some(Err(())) => { + candidate.inc_hex_len(); + continue; + } + None => return Ok(None), + } + } + Ok(Some(candidate.to_prefix())) + } + + /// Find the only object matching `prefix` and return it as `Ok(Some(Ok(<ObjectId>)))`, or return `Ok(Some(Err(()))` + /// if multiple different objects with the same prefix were found. + /// + /// Return `Ok(None)` if no object matched the `prefix`. + /// + /// Pass `candidates` to obtain the set of all object ids matching `prefix`, with the same return value as + /// one would have received if it remained `None`. + /// + /// ### Performance Note + /// + /// - Unless the handles refresh mode is set to `Never`, each lookup will trigger a refresh of the object databases files + /// on disk if the prefix doesn't lead to ambiguous results. + /// - Since all objects need to be examined to assure non-ambiguous return values, after calling this method all indices will + /// be loaded. + /// - If `candidates` is `Some(…)`, the traversal will continue to obtain all candidates, which takes more time + /// as there is no early abort. + pub fn lookup_prefix( + &self, + prefix: gix_hash::Prefix, + mut candidates: Option<&mut HashSet<gix_hash::ObjectId>>, + ) -> Result<Option<lookup::Outcome>, lookup::Error> { + let mut candidate: Option<gix_hash::ObjectId> = None; + loop { + let snapshot = self.snapshot.borrow(); + for index in snapshot.indices.iter() { + #[allow(clippy::needless_option_as_deref)] // needed as it's the equivalent of a reborrow. + let lookup_result = index.lookup_prefix(prefix, candidates.as_deref_mut()); + if candidates.is_none() && !check_candidate(lookup_result, &mut candidate) { + return Ok(Some(Err(()))); + } + } + + for lodb in snapshot.loose_dbs.iter() { + #[allow(clippy::needless_option_as_deref)] // needed as it's the equivalent of a reborrow. + let lookup_result = lodb.lookup_prefix(prefix, candidates.as_deref_mut())?; + if candidates.is_none() && !check_candidate(lookup_result, &mut candidate) { + return Ok(Some(Err(()))); + } + } + + match self.store.load_one_index(self.refresh, snapshot.marker)? { + Some(new_snapshot) => { + drop(snapshot); + *self.snapshot.borrow_mut() = new_snapshot; + } + None => { + return match &candidates { + Some(candidates) => match candidates.len() { + 0 => Ok(None), + 1 => Ok(candidates.iter().cloned().next().map(Ok)), + _ => Ok(Some(Err(()))), + }, + None => Ok(candidate.map(Ok)), + }; + } + } + } + + fn check_candidate(lookup_result: Option<lookup::Outcome>, candidate: &mut Option<gix_hash::ObjectId>) -> bool { + match (lookup_result, &*candidate) { + (Some(Ok(oid)), Some(candidate)) if *candidate != oid => false, + (Some(Ok(_)), Some(_)) | (None, None) | (None, Some(_)) => true, + (Some(Err(())), _) => false, + (Some(Ok(oid)), None) => { + *candidate = Some(oid); + true + } + } + } + } +} diff --git a/vendor/gix-odb/src/store_impls/dynamic/types.rs b/vendor/gix-odb/src/store_impls/dynamic/types.rs new file mode 100644 index 000000000..2bda0d7d3 --- /dev/null +++ b/vendor/gix-odb/src/store_impls/dynamic/types.rs @@ -0,0 +1,460 @@ +use std::{ + path::{Path, PathBuf}, + sync::{ + atomic::{AtomicU16, AtomicU32, AtomicUsize, Ordering}, + Arc, + }, + time::SystemTime, +}; + +use arc_swap::ArcSwap; +use gix_features::hash; + +/// An id to refer to an index file or a multipack index file +pub type IndexId = usize; +pub(crate) type StateId = u32; +pub(crate) type Generation = u32; +pub(crate) type AtomicGeneration = AtomicU32; + +/// A way to indicate which pack indices we have seen already and which of them are loaded, along with an idea +/// of whether stored `PackId`s are still usable. +#[derive(Default, Copy, Clone)] +pub struct SlotIndexMarker { + /// The generation the `loaded_until_index` belongs to. Indices of different generations are completely incompatible. + /// This value changes once the internal representation is compacted, something that may happen only if there is no handle + /// requiring stable pack indices. + pub(crate) generation: Generation, + /// A unique id identifying the index state as well as all loose databases we have last observed. + /// If it changes in any way, the value is different. + pub(crate) state_id: StateId, +} + +/// A way to load and refer to a pack uniquely, namespaced by their indexing mechanism, aka multi-pack or not. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct PackId { + /// This is the index in the slot map at which the packs index is located. + pub(crate) index: IndexId, + /// If the pack is in a multi-pack index, this additional index is the pack-index within the multi-pack index identified by `index`. + pub(crate) multipack_index: Option<gix_pack::multi_index::PackIndex>, +} + +impl PackId { + /// Returns the maximum of indices we can represent. + pub(crate) const fn max_indices() -> usize { + (1 << 15) - 1 + } + /// Returns the maximum of packs we can represent if stored in a multi-index. + pub(crate) const fn max_packs_in_multi_index() -> gix_pack::multi_index::PackIndex { + (1 << 16) - 1 + } + /// Packs have a built-in identifier to make data structures simpler, and this method represents ourselves as such id + /// to be convertible back and forth. We essentially compress ourselves into a u32. + /// + /// Bit 16 is a marker to tell us if it's a multi-pack or not, the ones before are the index file itself, the ones after + /// are used to encode the pack index within the multi-pack. + pub(crate) fn to_intrinsic_pack_id(self) -> gix_pack::data::Id { + assert!(self.index < (1 << 15), "There shouldn't be more than 2^15 indices"); + match self.multipack_index { + None => self.index as gix_pack::data::Id, + Some(midx) => { + assert!( + midx <= Self::max_packs_in_multi_index(), + "There shouldn't be more than 2^16 packs per multi-index" + ); + ((self.index as gix_pack::data::Id | 1 << 15) | midx << 16) as gix_pack::data::Id + } + } + } + + pub(crate) fn from_intrinsic_pack_id(pack_id: gix_pack::data::Id) -> Self { + if pack_id & (1 << 15) == 0 { + PackId { + index: (pack_id & 0x7fff) as IndexId, + multipack_index: None, + } + } else { + PackId { + index: (pack_id & 0x7fff) as IndexId, + multipack_index: Some(pack_id >> 16), + } + } + } +} + +/// An index that changes only if the packs directory changes and its contents is re-read. +#[derive(Default)] +pub struct SlotMapIndex { + /// The index into the slot map at which we expect an index or pack file. Neither of these might be loaded yet. + pub(crate) slot_indices: Vec<usize>, + /// A list of loose object databases as resolved by their alternates file in the `object_directory`. The first entry is this objects + /// directory loose file database. All other entries are the loose stores of alternates. + /// It's in an Arc to be shared to Handles, but not to be shared across SlotMapIndices. + pub(crate) loose_dbs: Arc<Vec<crate::loose::Store>>, + + /// A static value that doesn't ever change for a particular clone of this index. + pub(crate) generation: Generation, + /// The number of indices loaded thus far when the index of the slot map was last examined, which can change as new indices are loaded + /// in parallel. + /// Shared across SlotMapIndex instances of the same generation. + pub(crate) next_index_to_load: Arc<AtomicUsize>, + /// Incremented by one up to `slot_indices.len()` once an attempt to load an index completed. + /// If a load failed, there will also be an increment. + /// Shared across SlotMapIndex instances of the same generation. + pub(crate) loaded_indices: Arc<AtomicUsize>, + /// The amount of indices that are currently being loaded. + /// Zero if no loading operation is currently happening, or more otherwise. + pub(crate) num_indices_currently_being_loaded: Arc<AtomicU16>, +} + +impl SlotMapIndex { + pub(crate) fn state_id(self: &Arc<SlotMapIndex>) -> StateId { + // We let the loaded indices take part despite not being part of our own snapshot. + // This is to account for indices being loaded in parallel without actually changing the snapshot itself. + let hash = hash::crc32(&(Arc::as_ptr(self) as usize).to_be_bytes()); + hash::crc32_update(hash, &self.loaded_indices.load(Ordering::SeqCst).to_be_bytes()) + } + + pub(crate) fn marker(self: &Arc<SlotMapIndex>) -> SlotIndexMarker { + SlotIndexMarker { + generation: self.generation, + state_id: self.state_id(), + } + } + + /// Returns true if we already know at least one loose object db, a sign of being initialized + pub(crate) fn is_initialized(&self) -> bool { + !self.loose_dbs.is_empty() + } +} + +#[derive(Clone)] +pub(crate) struct OnDiskFile<T: Clone> { + /// The last known path of the file + path: Arc<PathBuf>, + /// the time the file was last modified + mtime: SystemTime, + state: OnDiskFileState<T>, +} + +#[derive(Clone)] +pub(crate) enum OnDiskFileState<T: Clone> { + /// The file is on disk and can be loaded from there. + Unloaded, + Loaded(T), + /// The file was loaded, but appeared to be missing on disk after reconciling our state with what's on disk. + /// As there were handles that required pack-id stability we had to keep the item to allow finding it on later + /// lookups. + Garbage(T), + /// File is missing on disk and could not be loaded when we tried or turned missing after reconciling our state. + Missing, +} + +impl<T: Clone> OnDiskFile<T> { + pub fn path(&self) -> &Path { + &self.path + } + /// Return true if we hold a memory map of the file already. + pub fn is_loaded(&self) -> bool { + matches!(self.state, OnDiskFileState::Loaded(_) | OnDiskFileState::Garbage(_)) + } + + /// Return true if we are to be collected as garbage + pub fn is_disposable(&self) -> bool { + matches!(self.state, OnDiskFileState::Garbage(_) | OnDiskFileState::Missing) + } + + // On error, always declare the file missing and return an error. + pub(crate) fn load_strict(&mut self, load: impl FnOnce(&Path) -> std::io::Result<T>) -> std::io::Result<()> { + use OnDiskFileState::*; + match self.state { + Unloaded | Missing => match load(&self.path) { + Ok(v) => { + self.state = Loaded(v); + Ok(()) + } + Err(err) => { + // TODO: Should be provide more information? We don't even know what exactly failed right now, degenerating information. + self.state = Missing; + Err(err) + } + }, + Loaded(_) | Garbage(_) => Ok(()), + } + } + /// If the file is missing, we don't consider this failure but instead return Ok(None) to allow recovery. + /// when we know that loading is necessary. This also works around borrow check, which is a nice coincidence. + pub fn load_with_recovery(&mut self, load: impl FnOnce(&Path) -> std::io::Result<T>) -> std::io::Result<Option<T>> { + use OnDiskFileState::*; + match &mut self.state { + Loaded(v) | Garbage(v) => Ok(Some(v.clone())), + Missing => Ok(None), + Unloaded => match load(&self.path) { + Ok(v) => { + self.state = OnDiskFileState::Loaded(v.clone()); + Ok(Some(v)) + } + Err(err) if err.kind() == std::io::ErrorKind::NotFound => { + self.state = OnDiskFileState::Missing; + Ok(None) + } + Err(err) => Err(err), + }, + } + } + + pub fn loaded(&self) -> Option<&T> { + use OnDiskFileState::*; + match &self.state { + Loaded(v) | Garbage(v) => Some(v), + Unloaded | Missing => None, + } + } + + pub fn put_back(&mut self) { + match std::mem::replace(&mut self.state, OnDiskFileState::Missing) { + OnDiskFileState::Garbage(v) => self.state = OnDiskFileState::Loaded(v), + OnDiskFileState::Missing => self.state = OnDiskFileState::Unloaded, + other @ OnDiskFileState::Loaded(_) | other @ OnDiskFileState::Unloaded => self.state = other, + } + } + + pub fn trash(&mut self) { + match std::mem::replace(&mut self.state, OnDiskFileState::Missing) { + OnDiskFileState::Loaded(v) => self.state = OnDiskFileState::Garbage(v), + other @ OnDiskFileState::Garbage(_) + | other @ OnDiskFileState::Unloaded + | other @ OnDiskFileState::Missing => self.state = other, + } + } +} + +#[derive(Clone)] +pub(crate) struct IndexFileBundle { + pub index: OnDiskFile<Arc<gix_pack::index::File>>, + pub data: OnDiskFile<Arc<gix_pack::data::File>>, +} + +#[derive(Clone)] +pub(crate) struct MultiIndexFileBundle { + pub multi_index: OnDiskFile<Arc<gix_pack::multi_index::File>>, + pub data: Vec<OnDiskFile<Arc<gix_pack::data::File>>>, +} + +#[derive(Clone)] +pub(crate) enum IndexAndPacks { + Index(IndexFileBundle), + /// Note that there can only be one multi-pack file per repository, but thanks to git alternates, there can be multiple overall. + MultiIndex(MultiIndexFileBundle), +} + +impl IndexAndPacks { + pub(crate) fn index_path(&self) -> &Path { + match self { + IndexAndPacks::Index(index) => &index.index.path, + IndexAndPacks::MultiIndex(index) => &index.multi_index.path, + } + } + + pub(crate) fn mtime(&self) -> SystemTime { + match self { + IndexAndPacks::Index(index) => index.index.mtime, + IndexAndPacks::MultiIndex(index) => index.multi_index.mtime, + } + } + + /// If we are garbage, put ourselves into the loaded state. Otherwise put ourselves back to unloaded. + pub(crate) fn put_back(&mut self) { + match self { + IndexAndPacks::Index(bundle) => { + bundle.index.put_back(); + bundle.data.put_back(); + } + IndexAndPacks::MultiIndex(bundle) => { + bundle.multi_index.put_back(); + for data in &mut bundle.data { + data.put_back(); + } + } + } + } + + // The inverse of `put_back()`, by trashing the content. + pub(crate) fn trash(&mut self) { + match self { + IndexAndPacks::Index(bundle) => { + bundle.index.trash(); + bundle.data.trash(); + } + IndexAndPacks::MultiIndex(bundle) => { + bundle.multi_index.trash(); + for data in &mut bundle.data { + data.trash(); + } + } + } + } + + pub(crate) fn index_is_loaded(&self) -> bool { + match self { + Self::Index(bundle) => bundle.index.is_loaded(), + Self::MultiIndex(bundle) => bundle.multi_index.is_loaded(), + } + } + + pub(crate) fn is_disposable(&self) -> bool { + match self { + Self::Index(bundle) => bundle.index.is_disposable() || bundle.data.is_disposable(), + Self::MultiIndex(bundle) => { + bundle.multi_index.is_disposable() || bundle.data.iter().any(|odf| odf.is_disposable()) + } + } + } + + pub(crate) fn load_index(&mut self, object_hash: gix_hash::Kind) -> std::io::Result<()> { + match self { + IndexAndPacks::Index(bundle) => bundle.index.load_strict(|path| { + gix_pack::index::File::at(path, object_hash) + .map(Arc::new) + .map_err(|err| match err { + gix_pack::index::init::Error::Io { source, .. } => source, + err => std::io::Error::new(std::io::ErrorKind::Other, err), + }) + }), + IndexAndPacks::MultiIndex(bundle) => { + bundle.multi_index.load_strict(|path| { + gix_pack::multi_index::File::at(path) + .map(Arc::new) + .map_err(|err| match err { + gix_pack::multi_index::init::Error::Io { source, .. } => source, + err => std::io::Error::new(std::io::ErrorKind::Other, err), + }) + })?; + if let Some(multi_index) = bundle.multi_index.loaded() { + bundle.data = Self::index_names_to_pack_paths(multi_index); + } + Ok(()) + } + } + } + + pub(crate) fn new_single(index_path: PathBuf, mtime: SystemTime) -> Self { + let data_path = index_path.with_extension("pack"); + Self::Index(IndexFileBundle { + index: OnDiskFile { + path: index_path.into(), + state: OnDiskFileState::Unloaded, + mtime, + }, + data: OnDiskFile { + path: data_path.into(), + state: OnDiskFileState::Unloaded, + mtime, + }, + }) + } + + pub(crate) fn new_multi_from_open_file(multi_index: Arc<gix_pack::multi_index::File>, mtime: SystemTime) -> Self { + let data = Self::index_names_to_pack_paths(&multi_index); + Self::MultiIndex(MultiIndexFileBundle { + multi_index: OnDiskFile { + path: Arc::new(multi_index.path().to_owned()), + state: OnDiskFileState::Loaded(multi_index), + mtime, + }, + data, + }) + } + + fn index_names_to_pack_paths( + multi_index: &gix_pack::multi_index::File, + ) -> Vec<OnDiskFile<Arc<gix_pack::data::File>>> { + let parent_dir = multi_index.path().parent().expect("parent present"); + let data = multi_index + .index_names() + .iter() + .map(|idx| OnDiskFile { + path: parent_dir.join(idx.with_extension("pack")).into(), + state: OnDiskFileState::Unloaded, + mtime: SystemTime::UNIX_EPOCH, + }) + .collect(); + data + } +} + +#[derive(Default)] +pub(crate) struct MutableIndexAndPack { + pub(crate) files: ArcSwap<Option<IndexAndPacks>>, + pub(crate) write: parking_lot::Mutex<()>, + /// The generation required at least to read this slot. If these mismatch, the caller is likely referring to a now changed slot + /// that has different content under the same id. + /// Must only be changed when the write lock is held. + pub(crate) generation: AtomicGeneration, +} + +/// A snapshot about resource usage. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] +pub struct Metrics { + /// The total amount of handles which can be used to access object information. + pub num_handles: usize, + /// The amount of refreshes performed to reconcile with the ODB state on disk. + pub num_refreshes: usize, + /// The amount of indices that are currently open and will be returned to handles. + pub open_reachable_indices: usize, + /// The amount of reachable, known indices, which aren't opened yet. + pub known_reachable_indices: usize, + /// The amount of packs which are open in memory and will be returned to handles. + pub open_reachable_packs: usize, + /// The amount of packs that are reachable and will be returned to handles. They aren't open yet. + pub known_packs: usize, + /// The amount of slots which are empty. + /// + /// Over time these will fill, but they can be emptied as files are removed from disk. + pub unused_slots: usize, + /// Unreachable indices are still using slots, but aren't returned to new handles anymore unless they still happen to + /// know their id. + /// + /// This allows to keep files available while they are still potentially required for operations like pack generation, despite + /// the file on disk being removed or changed. + pub unreachable_indices: usize, + /// Equivalent to `unreachable_indices`, but for mapped packed data files + pub unreachable_packs: usize, + /// The amount of loose object databases currently available for object retrieval. + /// + /// There may be more than one if 'alternates' are used. + pub loose_dbs: usize, +} + +#[cfg(test)] +mod tests { + use super::*; + + mod pack_id { + use super::PackId; + + #[test] + fn to_intrinsic_roundtrip() { + let single = PackId { + index: (1 << 15) - 1, + multipack_index: None, + }; + let multi = PackId { + index: (1 << 15) - 1, + multipack_index: Some((1 << 16) - 1), + }; + assert_eq!(PackId::from_intrinsic_pack_id(single.to_intrinsic_pack_id()), single); + assert_eq!(PackId::from_intrinsic_pack_id(multi.to_intrinsic_pack_id()), multi); + } + + #[test] + #[should_panic] + fn max_supported_index_count() { + PackId { + index: 1 << 15, + multipack_index: None, + } + .to_intrinsic_pack_id(); + } + } +} diff --git a/vendor/gix-odb/src/store_impls/dynamic/verify.rs b/vendor/gix-odb/src/store_impls/dynamic/verify.rs new file mode 100644 index 000000000..9a35cb5d7 --- /dev/null +++ b/vendor/gix-odb/src/store_impls/dynamic/verify.rs @@ -0,0 +1,267 @@ +use std::{ + ops::Deref, + sync::atomic::{AtomicBool, Ordering}, + time::Instant, +}; + +use gix_features::progress::{MessageLevel, Progress}; + +use crate::{ + pack, + store::verify::integrity::{IndexStatistics, SingleOrMultiStatistics}, + types::IndexAndPacks, +}; + +/// +pub mod integrity { + use std::{marker::PhantomData, path::PathBuf}; + + use crate::pack; + + /// Options for use in [`Store::verify_integrity()`][crate::Store::verify_integrity()]. + pub type Options<F> = pack::index::verify::integrity::Options<F>; + + /// Returned by [`Store::verify_integrity()`][crate::Store::verify_integrity()]. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error(transparent)] + MultiIndexIntegrity(#[from] pack::index::traverse::Error<pack::multi_index::verify::integrity::Error>), + #[error(transparent)] + IndexIntegrity(#[from] pack::index::traverse::Error<pack::index::verify::integrity::Error>), + #[error(transparent)] + IndexOpen(#[from] pack::index::init::Error), + #[error(transparent)] + LooseObjectStoreIntegrity(#[from] crate::loose::verify::integrity::Error), + #[error(transparent)] + MultiIndexOpen(#[from] pack::multi_index::init::Error), + #[error(transparent)] + PackOpen(#[from] pack::data::init::Error), + #[error(transparent)] + InitializeODB(#[from] crate::store::load_index::Error), + #[error("The disk on state changed while performing the operation, and we observed the change.")] + NeedsRetryDueToChangeOnDisk, + } + + #[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Clone)] + #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] + /// Integrity information about loose object databases + pub struct LooseObjectStatistics { + /// The path to the root directory of the loose objects database + pub path: PathBuf, + /// The statistics created after verifying the loose object database. + pub statistics: crate::loose::verify::integrity::Statistics, + } + + #[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Clone)] + #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] + /// Traversal statistics of packs governed by single indices or multi-pack indices. + #[allow(missing_docs)] + pub enum SingleOrMultiStatistics { + Single(pack::index::traverse::Statistics), + Multi(Vec<(PathBuf, pack::index::traverse::Statistics)>), + } + + /// Statistics gathered when traversing packs of various kinds of indices. + #[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Clone)] + #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] + pub struct IndexStatistics { + /// The path to the index or multi-pack index for which statics were gathered. + pub path: PathBuf, + /// The actual statistics for the index at `path`. + pub statistics: SingleOrMultiStatistics, + } + + /// Returned by [`Store::verify_integrity()`][crate::Store::verify_integrity()]. + pub struct Outcome<P> { + /// Statistics for validated loose object stores. + pub loose_object_stores: Vec<LooseObjectStatistics>, + /// Pack traversal statistics for each index and their pack(s) + pub index_statistics: Vec<IndexStatistics>, + /// The provided progress instance. + pub progress: P, + } + + /// The progress ids used in [`Store::verify_integrity()`][crate::Store::verify_integrity()]. + /// + /// Use this information to selectively extract the progress of interest in case the parent application has custom visualization. + #[derive(Debug, Copy, Clone)] + pub enum ProgressId { + /// Contains the path of the currently validated loose object database. + VerifyLooseObjectDbPath, + /// The root progress for all verification of an index. It doesn't contain any useful information itself. + VerifyIndex(PhantomData<gix_pack::index::verify::integrity::ProgressId>), + /// The root progress for all verification of a multi-index. It doesn't contain any useful information itself. + VerifyMultiIndex(PhantomData<gix_pack::multi_index::verify::integrity::ProgressId>), + } + + impl From<ProgressId> for gix_features::progress::Id { + fn from(v: ProgressId) -> Self { + match v { + ProgressId::VerifyLooseObjectDbPath => *b"VISP", + ProgressId::VerifyMultiIndex(_) => *b"VIMI", + ProgressId::VerifyIndex(_) => *b"VISI", + } + } + } +} + +impl super::Store { + /// Check the integrity of all objects as per the given `options`. + /// + /// Note that this will not not force loading all indices or packs permanently, as we will only use the momentarily loaded disk state. + /// This does, however, include all alternates. + pub fn verify_integrity<C, P, F>( + &self, + mut progress: P, + should_interrupt: &AtomicBool, + options: integrity::Options<F>, + ) -> Result<integrity::Outcome<P>, integrity::Error> + where + P: Progress, + C: pack::cache::DecodeEntry, + F: Fn() -> C + Send + Clone, + { + let mut index = self.index.load(); + if !index.is_initialized() { + self.consolidate_with_disk_state(true, false)?; + index = self.index.load(); + assert!( + index.is_initialized(), + "BUG: after consolidating successfully, we have an initialized index" + ) + } + + progress.init( + Some(index.slot_indices.len()), + gix_features::progress::count("pack indices"), + ); + let mut statistics = Vec::new(); + let index_check_message = |path: &std::path::Path| { + format!( + "Checking integrity: {}", + path.file_name() + .map(|f| f.to_string_lossy()) + .unwrap_or_else(std::borrow::Cow::default) + ) + }; + for slot_index in &index.slot_indices { + let slot = &self.files[*slot_index]; + if slot.generation.load(Ordering::SeqCst) != index.generation { + return Err(integrity::Error::NeedsRetryDueToChangeOnDisk); + } + let files = slot.files.load(); + let files = Option::as_ref(&files).ok_or(integrity::Error::NeedsRetryDueToChangeOnDisk)?; + + let start = Instant::now(); + let (mut child_progress, num_objects, index_path) = match files { + IndexAndPacks::Index(bundle) => { + let index; + let index = match bundle.index.loaded() { + Some(index) => index.deref(), + None => { + index = pack::index::File::at(bundle.index.path(), self.object_hash)?; + &index + } + }; + let pack; + let data = match bundle.data.loaded() { + Some(pack) => pack.deref(), + None => { + pack = pack::data::File::at(bundle.data.path(), self.object_hash)?; + &pack + } + }; + let outcome = index.verify_integrity( + Some(pack::index::verify::PackContext { + data, + options: options.clone(), + }), + progress.add_child_with_id( + "verify index", + integrity::ProgressId::VerifyIndex(Default::default()).into(), + ), + should_interrupt, + )?; + statistics.push(IndexStatistics { + path: bundle.index.path().to_owned(), + statistics: SingleOrMultiStatistics::Single( + outcome + .pack_traverse_statistics + .expect("pack provided so there are stats"), + ), + }); + (outcome.progress, index.num_objects(), index.path().to_owned()) + } + IndexAndPacks::MultiIndex(bundle) => { + let index; + let index = match bundle.multi_index.loaded() { + Some(index) => index.deref(), + None => { + index = pack::multi_index::File::at(bundle.multi_index.path())?; + &index + } + }; + let outcome = index.verify_integrity( + progress.add_child_with_id( + "verify multi-index", + integrity::ProgressId::VerifyMultiIndex(Default::default()).into(), + ), + should_interrupt, + options.clone(), + )?; + + let index_dir = bundle.multi_index.path().parent().expect("file in a directory"); + statistics.push(IndexStatistics { + path: Default::default(), + statistics: SingleOrMultiStatistics::Multi( + outcome + .pack_traverse_statistics + .into_iter() + .zip(index.index_names()) + .map(|(statistics, index_name)| (index_dir.join(index_name), statistics)) + .collect(), + ), + }); + (outcome.progress, index.num_objects(), index.path().to_owned()) + } + }; + + child_progress.set_name(index_check_message(&index_path)); + child_progress.show_throughput_with( + start, + num_objects as usize, + gix_features::progress::count("objects").expect("set"), + MessageLevel::Success, + ); + progress.inc(); + } + + progress.init( + Some(index.loose_dbs.len()), + gix_features::progress::count("loose object stores"), + ); + let mut loose_object_stores = Vec::new(); + for loose_db in &*index.loose_dbs { + let out = loose_db + .verify_integrity( + progress.add_child_with_id( + loose_db.path().display().to_string(), + integrity::ProgressId::VerifyLooseObjectDbPath.into(), + ), + should_interrupt, + ) + .map(|statistics| integrity::LooseObjectStatistics { + path: loose_db.path().to_owned(), + statistics, + })?; + loose_object_stores.push(out); + } + + Ok(integrity::Outcome { + loose_object_stores, + index_statistics: statistics, + progress, + }) + } +} diff --git a/vendor/gix-odb/src/store_impls/dynamic/write.rs b/vendor/gix-odb/src/store_impls/dynamic/write.rs new file mode 100644 index 000000000..a2e40eec4 --- /dev/null +++ b/vendor/gix-odb/src/store_impls/dynamic/write.rs @@ -0,0 +1,47 @@ +use std::{io::Read, ops::Deref}; + +use gix_hash::ObjectId; +use gix_object::Kind; + +use crate::store; + +mod error { + use crate::{loose, store}; + + /// The error returned by the [dynamic Store's][crate::Store] [`Write`][crate::Write] implementation. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error(transparent)] + LoadIndex(#[from] store::load_index::Error), + #[error(transparent)] + LooseWrite(#[from] loose::write::Error), + #[error(transparent)] + Io(#[from] std::io::Error), + } +} +pub use error::Error; + +use crate::store_impls::dynamic; + +impl<S> crate::Write for store::Handle<S> +where + S: Deref<Target = dynamic::Store> + Clone, +{ + type Error = Error; + + fn write_stream(&self, kind: Kind, size: u64, from: impl Read) -> Result<ObjectId, Self::Error> { + let mut snapshot = self.snapshot.borrow_mut(); + Ok(match snapshot.loose_dbs.first() { + Some(ldb) => ldb.write_stream(kind, size, from)?, + None => { + let new_snapshot = self + .store + .load_one_index(self.refresh, snapshot.marker)? + .expect("there is always at least one ODB, and this code runs only once for initialization"); + *snapshot = new_snapshot; + snapshot.loose_dbs[0].write_stream(kind, size, from)? + } + }) + } +} diff --git a/vendor/gix-odb/src/store_impls/loose/find.rs b/vendor/gix-odb/src/store_impls/loose/find.rs new file mode 100644 index 000000000..13bd26818 --- /dev/null +++ b/vendor/gix-odb/src/store_impls/loose/find.rs @@ -0,0 +1,262 @@ +use std::{cmp::Ordering, collections::HashSet, fs, io::Read, path::PathBuf}; + +use gix_features::zlib; + +use crate::store_impls::loose::{hash_path, Store, HEADER_MAX_SIZE}; + +/// Returned by [`Store::try_find()`] +#[derive(thiserror::Error, Debug)] +#[allow(missing_docs)] +pub enum Error { + #[error("decompression of loose object at '{path}' failed")] + DecompressFile { + source: zlib::inflate::Error, + path: PathBuf, + }, + #[error("file at '{path}' showed invalid size of inflated data, expected {expected}, got {actual}")] + SizeMismatch { + actual: usize, + expected: usize, + path: PathBuf, + }, + #[error(transparent)] + Decode(#[from] gix_object::decode::LooseHeaderDecodeError), + #[error("Could not {action} data at '{path}'")] + Io { + source: std::io::Error, + action: &'static str, + path: PathBuf, + }, +} + +/// Object lookup +impl Store { + const OPEN_ACTION: &'static str = "open"; + + /// Returns true if the given id is contained in our repository. + pub fn contains(&self, id: impl AsRef<gix_hash::oid>) -> bool { + debug_assert_eq!(self.object_hash, id.as_ref().kind()); + hash_path(id.as_ref(), self.path.clone()).is_file() + } + + /// Given a `prefix`, find an object that matches it uniquely within this loose object + /// database as `Ok(Some(Ok(<oid>)))`. + /// If there is more than one object matching the object `Ok(Some(Err(()))` is returned. + /// + /// Finally, if no object matches, the return value is `Ok(None)`. + /// + /// The outer `Result` is to indicate errors during file system traversal. + /// + /// Pass `candidates` to obtain the set of all object ids matching `prefix`, with the same return value as + /// one would have received if it remained `None`. + pub fn lookup_prefix( + &self, + prefix: gix_hash::Prefix, + mut candidates: Option<&mut HashSet<gix_hash::ObjectId>>, + ) -> Result<Option<crate::store::prefix::lookup::Outcome>, crate::loose::iter::Error> { + let single_directory_iter = crate::loose::Iter { + inner: gix_features::fs::walkdir_new( + self.path.join(prefix.as_oid().to_hex_with_len(2).to_string()), + gix_features::fs::walkdir::Parallelism::Serial, + ) + .min_depth(1) + .max_depth(1) + .follow_links(false) + .into_iter(), + hash_hex_len: prefix.as_oid().kind().len_in_hex(), + }; + let mut candidate = None; + for oid in single_directory_iter { + let oid = match oid { + Ok(oid) => oid, + Err(err) => { + return match err.io_error() { + Some(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None), + None | Some(_) => Err(err), + } + } + }; + if prefix.cmp_oid(&oid) == Ordering::Equal { + match &mut candidates { + Some(candidates) => { + candidates.insert(oid); + } + None => { + if candidate.is_some() { + return Ok(Some(Err(()))); + } + candidate = Some(oid); + } + } + } + } + + match &mut candidates { + Some(candidates) => match candidates.len() { + 0 => Ok(None), + 1 => Ok(candidates.iter().next().cloned().map(Ok)), + _ => Ok(Some(Err(()))), + }, + None => Ok(candidate.map(Ok)), + } + } + + /// Return the object identified by the given [`ObjectId`][gix_hash::ObjectId] if present in this database, + /// writing its raw data into the given `out` buffer. + /// + /// Returns `Err` if there was an error locating or reading the object. Returns `Ok<None>` if + /// there was no such object. + pub fn try_find<'a>( + &self, + id: impl AsRef<gix_hash::oid>, + out: &'a mut Vec<u8>, + ) -> Result<Option<gix_object::Data<'a>>, Error> { + debug_assert_eq!(self.object_hash, id.as_ref().kind()); + match self.find_inner(id.as_ref(), out) { + Ok(obj) => Ok(Some(obj)), + Err(err) => match err { + Error::Io { + source: err, + action, + path, + } => { + if action == Self::OPEN_ACTION && err.kind() == std::io::ErrorKind::NotFound { + Ok(None) + } else { + Err(Error::Io { + source: err, + action, + path, + }) + } + } + err => Err(err), + }, + } + } + + /// Return only the decompressed size of the object and its kind without fully reading it into memory as tuple of `(size, kind)`. + /// Returns `None` if `id` does not exist in the database. + pub fn try_header(&self, id: impl AsRef<gix_hash::oid>) -> Result<Option<(usize, gix_object::Kind)>, Error> { + const BUF_SIZE: usize = 256; + let mut buf = [0_u8; BUF_SIZE]; + let path = hash_path(id.as_ref(), self.path.clone()); + + let mut inflate = zlib::Inflate::default(); + let mut istream = match fs::File::open(&path) { + Ok(f) => f, + Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(err) => { + return Err(Error::Io { + source: err, + action: Self::OPEN_ACTION, + path, + }) + } + }; + + let (compressed_buf, _) = buf.split_at_mut(BUF_SIZE - HEADER_MAX_SIZE); + let bytes_read = istream.read(compressed_buf).map_err(|e| Error::Io { + source: e, + action: "read", + path: path.to_owned(), + })?; + let (compressed_buf, header_buf) = buf.split_at_mut(bytes_read); + let (status, _consumed_in, consumed_out) = + inflate + .once(compressed_buf, header_buf) + .map_err(|e| Error::DecompressFile { + source: e, + path: path.to_owned(), + })?; + + if status == zlib::Status::BufError { + return Err(Error::DecompressFile { + source: zlib::inflate::Error::Status(status), + path, + }); + } + let (kind, size, _header_size) = gix_object::decode::loose_header(&header_buf[..consumed_out])?; + Ok(Some((size, kind))) + } + + fn find_inner<'a>(&self, id: &gix_hash::oid, buf: &'a mut Vec<u8>) -> Result<gix_object::Data<'a>, Error> { + let path = hash_path(id, self.path.clone()); + + let mut inflate = zlib::Inflate::default(); + let ((status, consumed_in, consumed_out), bytes_read) = { + let mut istream = fs::File::open(&path).map_err(|e| Error::Io { + source: e, + action: Self::OPEN_ACTION, + path: path.to_owned(), + })?; + + buf.clear(); + let bytes_read = istream.read_to_end(buf).map_err(|e| Error::Io { + source: e, + action: "read", + path: path.to_owned(), + })?; + buf.resize(bytes_read + HEADER_MAX_SIZE, 0); + let (input, output) = buf.split_at_mut(bytes_read); + ( + inflate + .once(&input[..bytes_read], output) + .map_err(|e| Error::DecompressFile { + source: e, + path: path.to_owned(), + })?, + bytes_read, + ) + }; + if status == zlib::Status::BufError { + return Err(Error::DecompressFile { + source: zlib::inflate::Error::Status(status), + path, + }); + } + + let decompressed_start = bytes_read; + let (kind, size, header_size) = + gix_object::decode::loose_header(&buf[decompressed_start..decompressed_start + consumed_out])?; + + if status == zlib::Status::StreamEnd { + let decompressed_body_bytes_sans_header = + decompressed_start + header_size..decompressed_start + consumed_out; + + if consumed_out != size + header_size { + return Err(Error::SizeMismatch { + expected: size + header_size, + actual: consumed_out, + path, + }); + } + buf.copy_within(decompressed_body_bytes_sans_header, 0); + } else { + buf.resize(bytes_read + size + header_size, 0); + { + let (input, output) = buf.split_at_mut(bytes_read); + let num_decompressed_bytes = zlib::stream::inflate::read( + &mut &input[consumed_in..], + &mut inflate.state, + &mut output[consumed_out..], + ) + .map_err(|e| Error::Io { + source: e, + action: "deflate", + path: path.to_owned(), + })?; + if num_decompressed_bytes + consumed_out != size + header_size { + return Err(Error::SizeMismatch { + expected: size + header_size, + actual: num_decompressed_bytes + consumed_out, + path, + }); + } + }; + buf.copy_within(decompressed_start + header_size.., 0); + } + buf.resize(size, 0); + Ok(gix_object::Data { kind, data: buf }) + } +} diff --git a/vendor/gix-odb/src/store_impls/loose/iter.rs b/vendor/gix-odb/src/store_impls/loose/iter.rs new file mode 100644 index 000000000..5904aeb12 --- /dev/null +++ b/vendor/gix-odb/src/store_impls/loose/iter.rs @@ -0,0 +1,81 @@ +use gix_features::fs; + +use crate::store_impls::loose; + +/// Returned by [`loose::Store::iter()`] +pub type Error = gix_features::fs::walkdir::Error; + +impl loose::Iter { + fn path_to_id( + &self, + res: Result<fs::walkdir::DirEntry, fs::walkdir::Error>, + ) -> Option<Result<gix_hash::ObjectId, Error>> { + use std::path::Component::Normal; + + match res { + Ok(e) => { + let p = e.path(); + let mut ci = p.components(); + let (c2, c1) = (ci.next_back(), ci.next_back()); + if let (Some(Normal(c1)), Some(Normal(c2))) = (c1, c2) { + if c1.len() == 2 && c2.len() == self.hash_hex_len - 2 { + if let (Some(c1), Some(c2)) = (c1.to_str(), c2.to_str()) { + let mut buf = gix_hash::Kind::hex_buf(); + { + let (first_byte, rest) = buf[..self.hash_hex_len].split_at_mut(2); + first_byte.copy_from_slice(c1.as_bytes()); + rest.copy_from_slice(c2.as_bytes()); + } + if let Ok(b) = gix_hash::ObjectId::from_hex(&buf[..self.hash_hex_len]) { + return Some(Ok(b)); + } + } + } + } + } + Err(err) => return Some(Err(err)), + }; + None + } +} + +impl Iterator for loose::Iter { + type Item = Result<gix_hash::ObjectId, Error>; + + fn next(&mut self) -> Option<Self::Item> { + while let Some(res) = self.inner.next() { + if let Some(res) = self.path_to_id(res) { + return Some(res); + } + } + None + } +} + +/// Iteration and traversal +impl loose::Store { + /// Return an iterator over all objects contained in the database. + /// + /// The [`Id`][gix_hash::ObjectId]s returned by the iterator can typically be used in the [`locate(…)`][loose::Store::try_find()] method. + /// _Note_ that the result is not sorted or stable, thus ordering can change between runs. + /// + /// # Notes + /// + /// [`loose::Iter`] is used instead of `impl Iterator<…>` to allow using this iterator in struct fields, as is currently + /// needed if iterators need to be implemented by hand in the absence of generators. + pub fn iter(&self) -> loose::Iter { + loose::Iter { + inner: fs::walkdir_new( + &self.path, + fs::walkdir::Parallelism::ThreadPoolPerTraversal { + thread_name: "gix_odb::loose::Store::iter: fs-walk", + }, + ) + .min_depth(2) + .max_depth(3) + .follow_links(false) + .into_iter(), + hash_hex_len: self.object_hash.len_in_hex(), + } + } +} diff --git a/vendor/gix-odb/src/store_impls/loose/mod.rs b/vendor/gix-odb/src/store_impls/loose/mod.rs new file mode 100644 index 000000000..17e4a33d6 --- /dev/null +++ b/vendor/gix-odb/src/store_impls/loose/mod.rs @@ -0,0 +1,66 @@ +//! An object database storing each object in a zlib compressed file with its hash in the path +/// The maximum size that an object header can have. `git2` says 64, and `git` says 32 but also mentions it can be larger. +const HEADER_MAX_SIZE: usize = 64; +use std::path::{Path, PathBuf}; + +use gix_features::fs; + +/// A database for reading and writing objects to disk, one file per object. +#[derive(Clone, PartialEq, Eq)] +pub struct Store { + /// The directory in which objects are stored, containing 256 folders representing the hashes first byte. + pub(crate) path: PathBuf, + /// The kind of hash we should assume during iteration and when writing new objects. + pub(crate) object_hash: gix_hash::Kind, +} + +/// Initialization +impl Store { + /// Initialize the Db with the `objects_directory` containing the hexadecimal first byte subdirectories, which in turn + /// contain all loose objects. + /// + /// In a git repository, this would be `.git/objects`. + /// + /// The `object_hash` determines which hash to use when writing, finding or iterating objects. + pub fn at(objects_directory: impl Into<PathBuf>, object_hash: gix_hash::Kind) -> Store { + Store { + path: objects_directory.into(), + object_hash, + } + } + + /// Return the path to our `objects` directory. + pub fn path(&self) -> &Path { + &self.path + } + + /// Return the kind of hash we would iterate and write. + pub fn object_hash(&self) -> gix_hash::Kind { + self.object_hash + } +} + +fn hash_path(id: &gix_hash::oid, mut root: PathBuf) -> PathBuf { + let mut hex = gix_hash::Kind::hex_buf(); + let hex_len = id.hex_to_buf(hex.as_mut()); + let buf = std::str::from_utf8(&hex[..hex_len]).expect("ascii only in hex"); + root.push(&buf[..2]); + root.push(&buf[2..]); + root +} + +/// +pub mod find; +/// +pub mod iter; +/// +pub mod verify; + +/// The type for an iterator over `Result<gix_hash::ObjectId, Error>)` +pub struct Iter { + inner: fs::walkdir::DirEntryIter, + hash_hex_len: usize, +} + +/// +pub mod write; diff --git a/vendor/gix-odb/src/store_impls/loose/verify.rs b/vendor/gix-odb/src/store_impls/loose/verify.rs new file mode 100644 index 000000000..648e5764c --- /dev/null +++ b/vendor/gix-odb/src/store_impls/loose/verify.rs @@ -0,0 +1,103 @@ +use std::{ + sync::atomic::{AtomicBool, Ordering}, + time::Instant, +}; + +use gix_features::progress::Progress; + +use crate::{loose::Store, Write}; + +/// +pub mod integrity { + /// The error returned by [`verify_integrity()`][super::Store::verify_integrity()]. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error("{kind} object {id} could not be decoded")] + ObjectDecode { + source: gix_object::decode::Error, + kind: gix_object::Kind, + id: gix_hash::ObjectId, + }, + #[error("{kind} object {expected} wasn't re-encoded without change - new hash is {actual}")] + ObjectHashMismatch { + kind: gix_object::Kind, + actual: gix_hash::ObjectId, + expected: gix_hash::ObjectId, + }, + #[error("Objects were deleted during iteration - try again")] + Retry, + #[error("Interrupted")] + Interrupted, + } + + /// The outcome returned by [`verify_integrity()`][super::Store::verify_integrity()]. + #[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Clone)] + #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] + pub struct Statistics { + /// The amount of loose objects we checked. + pub num_objects: usize, + } + + /// The progress ids used in [`verify_integrity()`][super::Store::verify_integrity()]. + /// + /// Use this information to selectively extract the progress of interest in case the parent application has custom visualization. + #[derive(Debug, Copy, Clone)] + pub enum ProgressId { + /// The amount of loose objects that have been verified. + LooseObjects, + } + + impl From<ProgressId> for gix_features::progress::Id { + fn from(v: ProgressId) -> Self { + match v { + ProgressId::LooseObjects => *b"VILO", + } + } + } +} + +impl Store { + /// Check all loose objects for their integrity checking their hash matches the actual data and by decoding them fully. + pub fn verify_integrity( + &self, + mut progress: impl Progress, + should_interrupt: &AtomicBool, + ) -> Result<integrity::Statistics, integrity::Error> { + let mut buf = Vec::new(); + let sink = crate::sink(self.object_hash); + + let mut num_objects = 0; + let start = Instant::now(); + let mut progress = progress.add_child_with_id("Validating", integrity::ProgressId::LooseObjects.into()); + progress.init(None, gix_features::progress::count("loose objects")); + for id in self.iter().filter_map(Result::ok) { + let object = self + .try_find(id, &mut buf) + .map_err(|_| integrity::Error::Retry)? + .ok_or(integrity::Error::Retry)?; + let actual_id = sink.write_buf(object.kind, object.data).expect("sink never fails"); + if actual_id != id { + return Err(integrity::Error::ObjectHashMismatch { + kind: object.kind, + actual: actual_id, + expected: id, + }); + } + object.decode().map_err(|err| integrity::Error::ObjectDecode { + source: err, + kind: object.kind, + id, + })?; + + progress.inc(); + num_objects += 1; + if should_interrupt.load(Ordering::SeqCst) { + return Err(integrity::Error::Interrupted); + } + } + progress.show_throughput(start); + + Ok(integrity::Statistics { num_objects }) + } +} diff --git a/vendor/gix-odb/src/store_impls/loose/write.rs b/vendor/gix-odb/src/store_impls/loose/write.rs new file mode 100644 index 000000000..e87462e4c --- /dev/null +++ b/vendor/gix-odb/src/store_impls/loose/write.rs @@ -0,0 +1,135 @@ +use std::{convert::TryInto, fs, io, io::Write, path::PathBuf}; + +use gix_features::{hash, zlib::stream::deflate}; +use gix_object::WriteTo; +use tempfile::NamedTempFile; + +use super::Store; +use crate::store_impls::loose; + +/// Returned by the [`crate::Write`] trait implementation of [`Store`] +#[derive(thiserror::Error, Debug)] +#[allow(missing_docs)] +pub enum Error { + #[error("Could not {message} '{path}'")] + Io { + source: io::Error, + message: &'static str, + path: PathBuf, + }, + #[error("An IO error occurred while writing an object")] + IoRaw(#[from] io::Error), + #[error("Could not turn temporary file into persisted file at '{target}'")] + Persist { + source: tempfile::PersistError, + target: PathBuf, + }, +} + +impl crate::traits::Write for Store { + type Error = Error; + + fn write(&self, object: impl WriteTo) -> Result<gix_hash::ObjectId, Self::Error> { + let mut to = self.dest()?; + to.write_all(&object.loose_header()).map_err(|err| Error::Io { + source: err, + message: "write header to tempfile in", + path: self.path.to_owned(), + })?; + object.write_to(&mut to).map_err(|err| Error::Io { + source: err, + message: "stream all data into tempfile in", + path: self.path.to_owned(), + })?; + to.flush()?; + self.finalize_object(to) + } + + /// Write the given buffer in `from` to disk in one syscall at best. + /// + /// This will cost at least 4 IO operations. + fn write_buf(&self, kind: gix_object::Kind, from: &[u8]) -> Result<gix_hash::ObjectId, Self::Error> { + let mut to = self.dest()?; + to.write_all(&gix_object::encode::loose_header(kind, from.len())) + .map_err(|err| Error::Io { + source: err, + message: "write header to tempfile in", + path: self.path.to_owned(), + })?; + + to.write_all(from).map_err(|err| Error::Io { + source: err, + message: "stream all data into tempfile in", + path: self.path.to_owned(), + })?; + to.flush()?; + self.finalize_object(to) + } + + /// Write the given stream in `from` to disk with at least one syscall. + /// + /// This will cost at least 4 IO operations. + fn write_stream( + &self, + kind: gix_object::Kind, + size: u64, + mut from: impl io::Read, + ) -> Result<gix_hash::ObjectId, Self::Error> { + let mut to = self.dest()?; + to.write_all(&gix_object::encode::loose_header( + kind, + size.try_into().expect("object size to fit into usize"), + )) + .map_err(|err| Error::Io { + source: err, + message: "write header to tempfile in", + path: self.path.to_owned(), + })?; + + io::copy(&mut from, &mut to).map_err(|err| Error::Io { + source: err, + message: "stream all data into tempfile in", + path: self.path.to_owned(), + })?; + to.flush()?; + self.finalize_object(to) + } +} + +type CompressedTempfile = deflate::Write<NamedTempFile>; + +impl Store { + fn dest(&self) -> Result<hash::Write<CompressedTempfile>, Error> { + Ok(hash::Write::new( + deflate::Write::new(NamedTempFile::new_in(&self.path).map_err(|err| Error::Io { + source: err, + message: "create named temp file in", + path: self.path.to_owned(), + })?), + self.object_hash, + )) + } + + fn finalize_object( + &self, + hash::Write { hash, inner: file }: hash::Write<CompressedTempfile>, + ) -> Result<gix_hash::ObjectId, Error> { + let id = gix_hash::ObjectId::from(hash.digest()); + let object_path = loose::hash_path(&id, self.path.clone()); + let object_dir = object_path + .parent() + .expect("each object path has a 1 hex-bytes directory"); + if let Err(err) = fs::create_dir(object_dir) { + match err.kind() { + io::ErrorKind::AlreadyExists => {} + _ => return Err(err.into()), + } + } + let file = file.into_inner(); + file.persist(&object_path).map_err(|err| Error::Persist { + source: err, + target: object_path, + })?; + Ok(id) + } +} diff --git a/vendor/gix-odb/src/store_impls/mod.rs b/vendor/gix-odb/src/store_impls/mod.rs new file mode 100644 index 000000000..4e3250508 --- /dev/null +++ b/vendor/gix-odb/src/store_impls/mod.rs @@ -0,0 +1,2 @@ +pub mod dynamic; +pub mod loose; diff --git a/vendor/gix-odb/src/traits.rs b/vendor/gix-odb/src/traits.rs new file mode 100644 index 000000000..ddec78b8e --- /dev/null +++ b/vendor/gix-odb/src/traits.rs @@ -0,0 +1,310 @@ +use std::io; + +use gix_object::WriteTo; + +/// Describe the capability to write git objects into an object store. +pub trait Write { + /// The error type used for all trait methods. + /// + /// _Note_ the default implementations require the `From<io::Error>` bound. + type Error: std::error::Error + From<io::Error>; + + /// Write objects using the intrinsic kind of [`hash`][gix_hash::Kind] into the database, + /// returning id to reference it in subsequent reads. + fn write(&self, object: impl WriteTo) -> Result<gix_hash::ObjectId, Self::Error> { + let mut buf = Vec::with_capacity(2048); + object.write_to(&mut buf)?; + self.write_stream(object.kind(), buf.len() as u64, buf.as_slice()) + } + /// As [`write`][Write::write], but takes an [`object` kind][gix_object::Kind] along with its encoded bytes. + fn write_buf(&self, object: gix_object::Kind, from: &[u8]) -> Result<gix_hash::ObjectId, Self::Error> { + self.write_stream(object, from.len() as u64, from) + } + /// As [`write`][Write::write], but takes an input stream. + /// This is commonly used for writing blobs directly without reading them to memory first. + fn write_stream( + &self, + kind: gix_object::Kind, + size: u64, + from: impl io::Read, + ) -> Result<gix_hash::ObjectId, Self::Error>; +} + +/// Describe how object can be located in an object store. +/// +/// ## Notes +/// +/// Find effectively needs [generic associated types][issue] to allow a trait for the returned object type. +/// Until then, we will have to make due with explicit types and give them the potentially added features we want. +/// +/// [issue]: https://github.com/rust-lang/rust/issues/44265 +pub trait Find { + /// The error returned by [`try_find()`][Find::try_find()] + type Error: std::error::Error + 'static; + + /// Returns true if the object exists in the database. + fn contains(&self, id: impl AsRef<gix_hash::oid>) -> bool; + + /// Find an object matching `id` in the database while placing its raw, possibly encoded data into `buffer`. + /// + /// Returns `Some` object if it was present in the database, or the error that occurred during lookup or object + /// retrieval. + fn try_find<'a>( + &self, + id: impl AsRef<gix_hash::oid>, + buffer: &'a mut Vec<u8>, + ) -> Result<Option<gix_object::Data<'a>>, Self::Error>; +} + +/// A way to obtain object properties without fully decoding it. +pub trait Header { + /// The error returned by [`try_header()`][Header::try_header()]. + type Error: std::error::Error + 'static; + /// Try to read the header of the object associated with `id` or return `None` if it could not be found. + fn try_header(&self, id: impl AsRef<gix_hash::oid>) -> Result<Option<find::Header>, Self::Error>; +} + +mod _impls { + use std::{io::Read, ops::Deref, rc::Rc, sync::Arc}; + + use gix_hash::{oid, ObjectId}; + use gix_object::{Data, Kind, WriteTo}; + + use crate::find::Header; + + impl<T> crate::Write for &T + where + T: crate::Write, + { + type Error = T::Error; + + fn write(&self, object: impl WriteTo) -> Result<ObjectId, Self::Error> { + (*self).write(object) + } + + fn write_buf(&self, object: Kind, from: &[u8]) -> Result<ObjectId, Self::Error> { + (*self).write_buf(object, from) + } + + fn write_stream(&self, kind: Kind, size: u64, from: impl Read) -> Result<ObjectId, Self::Error> { + (*self).write_stream(kind, size, from) + } + } + + impl<T> crate::Write for Arc<T> + where + T: crate::Write, + { + type Error = T::Error; + + fn write(&self, object: impl WriteTo) -> Result<ObjectId, Self::Error> { + self.deref().write(object) + } + + fn write_buf(&self, object: Kind, from: &[u8]) -> Result<ObjectId, Self::Error> { + self.deref().write_buf(object, from) + } + + fn write_stream(&self, kind: Kind, size: u64, from: impl Read) -> Result<ObjectId, Self::Error> { + self.deref().write_stream(kind, size, from) + } + } + + impl<T> crate::Write for Rc<T> + where + T: crate::Write, + { + type Error = T::Error; + + fn write(&self, object: impl WriteTo) -> Result<ObjectId, Self::Error> { + self.deref().write(object) + } + + fn write_buf(&self, object: Kind, from: &[u8]) -> Result<ObjectId, Self::Error> { + self.deref().write_buf(object, from) + } + + fn write_stream(&self, kind: Kind, size: u64, from: impl Read) -> Result<ObjectId, Self::Error> { + self.deref().write_stream(kind, size, from) + } + } + + impl<T> crate::Find for &T + where + T: crate::Find, + { + type Error = T::Error; + + fn contains(&self, id: impl AsRef<oid>) -> bool { + (*self).contains(id) + } + + fn try_find<'a>(&self, id: impl AsRef<oid>, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>, Self::Error> { + (*self).try_find(id, buffer) + } + } + + impl<T> crate::Header for &T + where + T: crate::Header, + { + type Error = T::Error; + + fn try_header(&self, id: impl AsRef<oid>) -> Result<Option<Header>, Self::Error> { + (*self).try_header(id) + } + } + + impl<T> crate::Find for Rc<T> + where + T: crate::Find, + { + type Error = T::Error; + + fn contains(&self, id: impl AsRef<oid>) -> bool { + self.deref().contains(id) + } + + fn try_find<'a>(&self, id: impl AsRef<oid>, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>, Self::Error> { + self.deref().try_find(id, buffer) + } + } + + impl<T> crate::Header for Rc<T> + where + T: crate::Header, + { + type Error = T::Error; + + fn try_header(&self, id: impl AsRef<oid>) -> Result<Option<Header>, Self::Error> { + self.deref().try_header(id) + } + } + + impl<T> crate::Find for Arc<T> + where + T: crate::Find, + { + type Error = T::Error; + + fn contains(&self, id: impl AsRef<oid>) -> bool { + self.deref().contains(id) + } + + fn try_find<'a>(&self, id: impl AsRef<oid>, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>, Self::Error> { + self.deref().try_find(id, buffer) + } + } + + impl<T> crate::Header for Arc<T> + where + T: crate::Header, + { + type Error = T::Error; + + fn try_header(&self, id: impl AsRef<oid>) -> Result<Option<Header>, Self::Error> { + self.deref().try_header(id) + } + } +} + +mod ext { + use gix_object::{BlobRef, CommitRef, CommitRefIter, Kind, ObjectRef, TagRef, TagRefIter, TreeRef, TreeRefIter}; + + use crate::find; + + macro_rules! make_obj_lookup { + ($method:ident, $object_variant:path, $object_kind:path, $object_type:ty) => { + /// Like [`find(…)`][Self::find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error + /// while returning the desired object type. + fn $method<'a>( + &self, + id: impl AsRef<gix_hash::oid>, + buffer: &'a mut Vec<u8>, + ) -> Result<$object_type, find::existing_object::Error<Self::Error>> { + let id = id.as_ref(); + self.try_find(id, buffer) + .map_err(find::existing_object::Error::Find)? + .ok_or_else(|| find::existing_object::Error::NotFound { + oid: id.as_ref().to_owned(), + }) + .and_then(|o| o.decode().map_err(find::existing_object::Error::Decode)) + .and_then(|o| match o { + $object_variant(o) => return Ok(o), + _other => Err(find::existing_object::Error::ObjectKind { + expected: $object_kind, + }), + }) + } + }; + } + + macro_rules! make_iter_lookup { + ($method:ident, $object_kind:path, $object_type:ty, $into_iter:tt) => { + /// Like [`find(…)`][Self::find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error + /// while returning the desired iterator type. + fn $method<'a>( + &self, + id: impl AsRef<gix_hash::oid>, + buffer: &'a mut Vec<u8>, + ) -> Result<$object_type, find::existing_iter::Error<Self::Error>> { + let id = id.as_ref(); + self.try_find(id, buffer) + .map_err(find::existing_iter::Error::Find)? + .ok_or_else(|| find::existing_iter::Error::NotFound { + oid: id.as_ref().to_owned(), + }) + .and_then(|o| { + o.$into_iter() + .ok_or_else(|| find::existing_iter::Error::ObjectKind { + expected: $object_kind, + }) + }) + } + }; + } + + /// An extension trait with convenience functions. + pub trait HeaderExt: super::Header { + /// Like [`try_header(…)`][super::Header::try_header()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error. + fn header( + &self, + id: impl AsRef<gix_hash::oid>, + ) -> Result<crate::find::Header, find::existing::Error<Self::Error>> { + let id = id.as_ref(); + self.try_header(id) + .map_err(find::existing::Error::Find)? + .ok_or_else(|| find::existing::Error::NotFound { oid: id.to_owned() }) + } + } + + impl<T: super::Header> HeaderExt for T {} + + /// An extension trait with convenience functions. + pub trait FindExt: super::Find { + /// Like [`try_find(…)`][super::Find::try_find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error. + fn find<'a>( + &self, + id: impl AsRef<gix_hash::oid>, + buffer: &'a mut Vec<u8>, + ) -> Result<gix_object::Data<'a>, find::existing::Error<Self::Error>> { + let id = id.as_ref(); + self.try_find(id, buffer) + .map_err(find::existing::Error::Find)? + .ok_or_else(|| find::existing::Error::NotFound { oid: id.to_owned() }) + } + + make_obj_lookup!(find_commit, ObjectRef::Commit, Kind::Commit, CommitRef<'a>); + make_obj_lookup!(find_tree, ObjectRef::Tree, Kind::Tree, TreeRef<'a>); + make_obj_lookup!(find_tag, ObjectRef::Tag, Kind::Tag, TagRef<'a>); + make_obj_lookup!(find_blob, ObjectRef::Blob, Kind::Blob, BlobRef<'a>); + make_iter_lookup!(find_commit_iter, Kind::Commit, CommitRefIter<'a>, try_into_commit_iter); + make_iter_lookup!(find_tree_iter, Kind::Tree, TreeRefIter<'a>, try_into_tree_iter); + make_iter_lookup!(find_tag_iter, Kind::Tag, TagRefIter<'a>, try_into_tag_iter); + } + + impl<T: super::Find> FindExt for T {} +} +pub use ext::{FindExt, HeaderExt}; + +use crate::find; |