summaryrefslogtreecommitdiffstats
path: root/vendor/gix-odb
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-odb')
-rw-r--r--vendor/gix-odb/.cargo-checksum.json1
-rw-r--r--vendor/gix-odb/CHANGELOG.md2302
-rw-r--r--vendor/gix-odb/Cargo.toml114
-rw-r--r--vendor/gix-odb/src/alternate/mod.rs75
-rw-r--r--vendor/gix-odb/src/alternate/parse.rs33
-rw-r--r--vendor/gix-odb/src/cache.rs234
-rw-r--r--vendor/gix-odb/src/find.rs113
-rw-r--r--vendor/gix-odb/src/lib.rs152
-rw-r--r--vendor/gix-odb/src/sink.rs66
-rw-r--r--vendor/gix-odb/src/store_impls/dynamic/access.rs24
-rw-r--r--vendor/gix-odb/src/store_impls/dynamic/find.rs519
-rw-r--r--vendor/gix-odb/src/store_impls/dynamic/handle.rs399
-rw-r--r--vendor/gix-odb/src/store_impls/dynamic/header.rs189
-rw-r--r--vendor/gix-odb/src/store_impls/dynamic/init.rs126
-rw-r--r--vendor/gix-odb/src/store_impls/dynamic/iter.rs236
-rw-r--r--vendor/gix-odb/src/store_impls/dynamic/load_index.rs719
-rw-r--r--vendor/gix-odb/src/store_impls/dynamic/load_one.rs154
-rw-r--r--vendor/gix-odb/src/store_impls/dynamic/metrics.rs80
-rw-r--r--vendor/gix-odb/src/store_impls/dynamic/mod.rs188
-rw-r--r--vendor/gix-odb/src/store_impls/dynamic/prefix.rs196
-rw-r--r--vendor/gix-odb/src/store_impls/dynamic/types.rs460
-rw-r--r--vendor/gix-odb/src/store_impls/dynamic/verify.rs267
-rw-r--r--vendor/gix-odb/src/store_impls/dynamic/write.rs47
-rw-r--r--vendor/gix-odb/src/store_impls/loose/find.rs262
-rw-r--r--vendor/gix-odb/src/store_impls/loose/iter.rs81
-rw-r--r--vendor/gix-odb/src/store_impls/loose/mod.rs66
-rw-r--r--vendor/gix-odb/src/store_impls/loose/verify.rs103
-rw-r--r--vendor/gix-odb/src/store_impls/loose/write.rs135
-rw-r--r--vendor/gix-odb/src/store_impls/mod.rs2
-rw-r--r--vendor/gix-odb/src/traits.rs310
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, &current_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;