summaryrefslogtreecommitdiffstats
path: root/vendor/gix-transport
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
commit10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch)
treebdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/gix-transport
parentReleasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff)
downloadrustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz
rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix-transport')
-rw-r--r--vendor/gix-transport/.cargo-checksum.json1
-rw-r--r--vendor/gix-transport/CHANGELOG.md1494
-rw-r--r--vendor/gix-transport/Cargo.toml175
-rw-r--r--vendor/gix-transport/src/client/async_io/bufread_ext.rs126
-rw-r--r--vendor/gix-transport/src/client/async_io/connect.rs47
-rw-r--r--vendor/gix-transport/src/client/async_io/mod.rs13
-rw-r--r--vendor/gix-transport/src/client/async_io/request.rs103
-rw-r--r--vendor/gix-transport/src/client/async_io/traits.rs113
-rw-r--r--vendor/gix-transport/src/client/blocking_io/bufread_ext.rs114
-rw-r--r--vendor/gix-transport/src/client/blocking_io/connect.rs68
-rw-r--r--vendor/gix-transport/src/client/blocking_io/file.rs288
-rw-r--r--vendor/gix-transport/src/client/blocking_io/http/curl/mod.rs169
-rw-r--r--vendor/gix-transport/src/client/blocking_io/http/curl/remote.rs392
-rw-r--r--vendor/gix-transport/src/client/blocking_io/http/mod.rs512
-rw-r--r--vendor/gix-transport/src/client/blocking_io/http/redirect.rs66
-rw-r--r--vendor/gix-transport/src/client/blocking_io/http/reqwest/mod.rs28
-rw-r--r--vendor/gix-transport/src/client/blocking_io/http/reqwest/remote.rs263
-rw-r--r--vendor/gix-transport/src/client/blocking_io/http/traits.rs133
-rw-r--r--vendor/gix-transport/src/client/blocking_io/mod.rs20
-rw-r--r--vendor/gix-transport/src/client/blocking_io/request.rs79
-rw-r--r--vendor/gix-transport/src/client/blocking_io/ssh/mod.rs131
-rw-r--r--vendor/gix-transport/src/client/blocking_io/ssh/program_kind.rs127
-rw-r--r--vendor/gix-transport/src/client/blocking_io/ssh/tests.rs285
-rw-r--r--vendor/gix-transport/src/client/blocking_io/traits.rs101
-rw-r--r--vendor/gix-transport/src/client/capabilities.rs307
-rw-r--r--vendor/gix-transport/src/client/git/async_io.rs151
-rw-r--r--vendor/gix-transport/src/client/git/blocking_io.rs199
-rw-r--r--vendor/gix-transport/src/client/git/mod.rs177
-rw-r--r--vendor/gix-transport/src/client/mod.rs36
-rw-r--r--vendor/gix-transport/src/client/non_io_types.rs159
-rw-r--r--vendor/gix-transport/src/client/traits.rs115
-rw-r--r--vendor/gix-transport/src/lib.rs99
32 files changed, 6091 insertions, 0 deletions
diff --git a/vendor/gix-transport/.cargo-checksum.json b/vendor/gix-transport/.cargo-checksum.json
new file mode 100644
index 000000000..08ab5f134
--- /dev/null
+++ b/vendor/gix-transport/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"CHANGELOG.md":"42e325cc72b69e544f76f387cfa343dbd8b0618b3f57fe066b1474d70a533f5c","Cargo.toml":"e9ec51f24d98190505c53f38fce76de9dedbfc3afeefd7e4182f41612eec0483","src/client/async_io/bufread_ext.rs":"51f55866fbae6521a45db6c054c9d8dbd01a458dec939a4d071d8c1798a11db3","src/client/async_io/connect.rs":"d2f64a865612cdf2aefebc8c58c28b0d303e697819ae467b0145d18ab87fd359","src/client/async_io/mod.rs":"dbc880330eea5ab38d2ac7aa8d295352cac1011aa4f6c9d9216ccfb7fa79789d","src/client/async_io/request.rs":"ad260288ef4d118af83b9bb3ce34e0d4b4bdac0dd3e2225463a74b8d7b791708","src/client/async_io/traits.rs":"22f76033eb5c8a26bd26bb7a24b26471b64a446f64e9daa535ab9fcb8d837dfe","src/client/blocking_io/bufread_ext.rs":"b67b7d02288a415b65dc2ef70eb68cd5d14bc680709489929ab8de5c654f6f29","src/client/blocking_io/connect.rs":"3f2db12f9b39f919acead9db33b35edf2cc6b43fc7d1a64f1be3eca2347acb2a","src/client/blocking_io/file.rs":"ddac1d00660760994c0e1e1b78b13126e4d34543ec84b61caf3fb0449eba6612","src/client/blocking_io/http/curl/mod.rs":"4fa0027b0aabdb71676c5267d8dab446ccd81863dd80dc9ac8f43e7710099ca4","src/client/blocking_io/http/curl/remote.rs":"46e7e34f84d52e39b583c8c2b35c8cd08fcadee95ad6e04390edb4b7348798d9","src/client/blocking_io/http/mod.rs":"ac14fcd20c45d5be59e04cfb293844acd064be34b559fb16eb793e4b891055f6","src/client/blocking_io/http/redirect.rs":"1f6d57c8a87a9cb4c3699c53f5e05468d99b89d49532c1f805931dcdcff36c0b","src/client/blocking_io/http/reqwest/mod.rs":"08d09aefaddbd0049676bdbe403fca2282c9da4484a6d7cd8e84ec8a46060184","src/client/blocking_io/http/reqwest/remote.rs":"dfcebff86bbf77401e1f22152b570a125dc38c8e983df51e14f8bb1e2459586c","src/client/blocking_io/http/traits.rs":"fe44b23d6c18389c2befe1b163bc3e268302b32952b7e7fc1c8d88561d1e2ba6","src/client/blocking_io/mod.rs":"b3b09948dcad91f5e9060875e5096c3d4155e3fdf33af0415b04f9c6246adec2","src/client/blocking_io/request.rs":"4877c53b0940c44f8c1d7ed84a0186451a1ff0f1d8b4274346ef534baf6d7449","src/client/blocking_io/ssh/mod.rs":"e9aafcfe09e63e940a822e68f4eee60436cb22b82ef355417257bab78c7419db","src/client/blocking_io/ssh/program_kind.rs":"e573e45103ac3978da65cb87be3aac476fc40781a953d2c464f754fa79398cee","src/client/blocking_io/ssh/tests.rs":"a5092be07ede47fc2ce8abacf3c328fab16652896f23ef33541f1ffc440411e4","src/client/blocking_io/traits.rs":"efc43e28a680e941b4443c6d014b4ce8591e6214f8f88565fc6c6dd1d2bbfc43","src/client/capabilities.rs":"fc92ed668cf0ed8b710f2a49f83bc2b4712b43ccd23b6ea99bc58aecf4d4dba2","src/client/git/async_io.rs":"97b8dccc93bf5a62349aec7dc967acb59212ca66a49347564da50e4fc64864ec","src/client/git/blocking_io.rs":"42e3ee0f597cc31bd87027e21573ccfd4270b835795526f65ef193cff0da077c","src/client/git/mod.rs":"46d990e403f5f825fb0f9332d2ee1a3d8b33209d5a74fe8b473840bcf8e873c4","src/client/mod.rs":"563bb655c93af9dde121a6c8ddb94055aac862da5ac3e9d0420ca5eb21892387","src/client/non_io_types.rs":"ef5d423003298029c1abecae3090e5f4ef8aecad43643ec952ad1ce523067586","src/client/traits.rs":"8dd06180d15209ba7d878835858c1f9c8e74656faf303d01b8bcaf7a6512d6b4","src/lib.rs":"1a1a3bd82420faa766deaf8dd59aff44225e3172c9da339aedd93bff25028343"},"package":"d633947b36a2fbbc089195bdc71621158f1660c2ff2a6b12b0279c16e2f764bc"} \ No newline at end of file
diff --git a/vendor/gix-transport/CHANGELOG.md b/vendor/gix-transport/CHANGELOG.md
new file mode 100644
index 000000000..06da083c5
--- /dev/null
+++ b/vendor/gix-transport/CHANGELOG.md
@@ -0,0 +1,1494 @@
+# 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.27.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.26.0 (2023-03-01)
+
+A maintenance release without user-facing changes.
+
+### Commit Statistics
+
+<csr-read-only-do-not-edit/>
+
+ - 4 commits contributed to the release.
+ - 8 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))
+</details>
+
+## 0.25.6 (2023-02-20)
+
+### Bug Fixes
+
+ - <csr-id-135d317065aae87af302beb6c26bb6ca8e30b6aa/> compatibility with `bstr` v1.3, use `*.as_bytes()` instead of `.as_ref()`.
+ `as_ref()` relies on a known target type which isn't always present. However, once
+ there is only one implementation, that's no problem, but when that changes compilation
+ fails due to ambiguity.
+
+### Commit Statistics
+
+<csr-read-only-do-not-edit/>
+
+ - 2 commits contributed to the release.
+ - 2 days passed between releases.
+ - 1 commit was 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-date v0.4.3, gix-hash v0.10.3, gix-features v0.26.5, gix-actor v0.17.2, gix-glob v0.5.5, gix-path v0.7.2, gix-quote v0.4.2, gix-attributes v0.8.3, gix-validate v0.7.3, gix-object v0.26.2, gix-ref v0.24.1, gix-config v0.16.2, gix-command v0.2.4, gix-url v0.13.3, gix-credentials v0.9.2, gix-discover v0.13.1, gix-index v0.12.4, gix-mailmap v0.9.3, gix-pack v0.30.3, gix-packetline v0.14.3, gix-transport v0.25.6, gix-protocol v0.26.4, gix-revision v0.10.4, gix-refspec v0.7.3, gix-worktree v0.12.3, gix v0.36.1 ([`9604783`](https://github.com/Byron/gitoxide/commit/96047839a20a657a559376b0b14c65aeab96acbd))
+ - Compatibility with `bstr` v1.3, use `*.as_bytes()` instead of `.as_ref()`. ([`135d317`](https://github.com/Byron/gitoxide/commit/135d317065aae87af302beb6c26bb6ca8e30b6aa))
+</details>
+
+## 0.25.5 (2023-02-17)
+
+### 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.
+ - <csr-id-3ee602d4cddaed69a875b76d8c941f51615b7048/> failure to set the http.version isn't critical.
+ Failing to set the version isn't critical, and may indeed fail depending on the version
+ of libcurl we are built against.
+ Furthermore, `git` itself doesn't actually check for errors when configuring curl at all,
+ treating all or most flags as non-critical.
+
+### Commit Statistics
+
+<csr-read-only-do-not-edit/>
+
+ - 2 commits contributed to the release.
+ - 1 commit was 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-transport v0.25.5 ([`f872ba8`](https://github.com/Byron/gitoxide/commit/f872ba8271a5d632acc071e7a857ef19f7cf5610))
+ - Failure to set the http.version isn't critical. ([`3ee602d`](https://github.com/Byron/gitoxide/commit/3ee602d4cddaed69a875b76d8c941f51615b7048))
+</details>
+
+## 0.25.4 (2023-02-17)
+
+<csr-id-5bef0a00e8d01110c054a517f6d9696f981a7efc/>
+<csr-id-2f3725efcaa439db4e10ade1b9fbeb1258fd93c1/>
+<csr-id-f7f136dbe4f86e7dee1d54835c420ec07c96cd78/>
+<csr-id-533e887e80c5f7ede8392884562e1c5ba56fb9a8/>
+
+### Bug Fixes (BREAKING)
+
+ - <csr-id-08dcda254bc942dcc36d432cf7130c2ce4c6d54e/> `Capabiltiies::from_lines()` takes a single buffer.
+ That way it doesn't have to convert to `String` as intermediary
+ which may fail as illformed UTF8 might be present.
+
+ Performance wise, reading all lines ahead of time, it is the same
+ as it was before as it would collect all lines beforehand anyway.
+
+ We are also seeing only a few lines in V2, and in V1 it was
+ fully streaming already.
+ - <csr-id-4308a209dddcbb461c34d45fb9af8b4621d4600a/> `max-pure` now builds without any C build tooling due to lack of `openssl-sys`.
+ To make this work, we leave the `reqwest` configuration to downstream crates.
+ Note that this means downstream will have to select their TLS settings
+ themselves, so builds may fail after upgrade until this is done.
+
+ An example for a `reqwest` configuration can be found in the
+ `gitoxide` Cargo.toml in the root of the `gitoxide` repository.
+
+### New Features (BREAKING)
+
+ - <csr-id-6fa27642aa57613cae82ae680f02923dad25d474/> ptions for `client::connect()` and support for more than one ssh variant, including permission-denied detection.
+ Options can be passed down to `client::ssh::connect()` to further configure it
+ similar to what git itself offers. That way, it's possible to use different ssh commands
+ and support all of gits configuration options.
+
+ Support for multiple ssh variants was added to use them (and their flags) correctly.
+
+ Detection of permission-denied errors due to invalid credentials was added so re-authentication
+ in upper layers can be implemented.
+ - <csr-id-041eca547a6629c8540728eba95dbcd636285ba9/> make streaming otional for any reqwest.
+ One can now indicate when initiating a reqwest that the transport doesn't
+ have to stream the data, even though it will always be provided to an
+ `std::io::Write`.
+
+ Note that this is at the discretion of the transport implementation and streaming
+ might still be done despite it not being requested.
+
+ Note that the caller should set this 'streaming' flag if the upper bound of data
+ is high for keeping it in memory or can't be estimated. This is generally true
+ when sending packs.
+ - <csr-id-1204bfcaadd31ed198b923df05f19115da3754a4/> Provide support for reading packetlines directly.
+ The handshake response itself now provides a `ref` read implementation
+ with direct readline support. That way one can avoid having to go
+ through `String`.
+ - <csr-id-8e158c3f4056f59724fe91587157ef0daa517964/> interpret the FollowRedirects option for the curl HTTP backend.
+ This comes with changes to the `HTTP` trait which now requires a base-url
+ to be provided as well.
+ - <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-78ad3df64f2c016ba17b158bd9ab1d2341aab399/> add `fetch::Transport::configure` to generically configure any transport.
+ - <csr-id-32dc1829a5661f66396d109c8d0a8eaae6b1f532/> use `git-credentials` in `git-protocol`
+
+### Changed (BREAKING)
+
+ - <csr-id-07512db093e62d9b9185368bd3fa561cfcd1d1d2/> `client::TransportWithoutIO::to_url()` returns `Cow<'_, BStr>`.
+ That way, it's possible to efficiently return URLs in the right format,
+ or return generates ones as needed.
+ - <csr-id-fe2042bff9ae38bf76b76cef14986f9f76bded7d/> `client::TransportWithoutIO::to_url()` returns `BString`.
+ That way it will not be lossy in case the URL represents a path, which
+ is relevant for transports that refer to paths.
+
+ Note that this doesn't matter when the url actually is a URL, as it's
+ specified to be valid unicode (I think).
+ - <csr-id-759b5d482de048deb24d14043a173079914e7ac8/> `client::TransportV2Ext::invoke()` supports owned `capabilities`.
+ That way it's easier to pass custom agent strings when invoking.
+ - <csr-id-1cf66c4dbc7a0404701efe4335363c2636ce32f8/> `client::http::connect()` returns `Transport<Impl>` directly.
+ It' can't fail, so no need to return `Result<_, Infallible>`.
+ - <csr-id-99905bacace8aed42b16d43f0f04cae996cb971c/> upgrade `bstr` to `1.0.1`
+ - <csr-id-9509ce4faeca8b4e1527bac625370403495bb03c/> `client::connect()` supports anything that parses into a `git_url::Url`; turn http url back to &str
+ The http url is always valid UTF-8 and doesn't contain invalid paths,
+ thus we should have the type system reflect that.
+ - <csr-id-52e8c149ff17ce894cc30d03ead1988f52f0663e/> `client::connect()` now takes a `&BStr` as URL
+ - <csr-id-71a43d0bc12661efcb9c94697c704f700a3be488/> use `thiserror` instead of `quickerror`
+ - <csr-id-12589cc6f08e4d7aabae30bcdadaa0c2b4850229/> adapt to changes in `git-url` and use `BString` to represent URLs.
+ They can contain paths, which is why `String` can't repsent a URL
+ losslessly.
+
+ For HTTP urls these are ultimately UTF-8 strings though.
+
+### Other
+
+ - <csr-id-5bef0a00e8d01110c054a517f6d9696f981a7efc/> try to make the transport configurable after being boxed, but…
+ …that would force it to be 'static, which is something we excplicitly
+ cannot have. We need references to be contained within, if I remember
+ correctly.
+ - <csr-id-2f3725efcaa439db4e10ade1b9fbeb1258fd93c1/> make capabilities parsing public
+
+### New Features
+
+<csr-id-0a2b135d19ce1f1b4b0394befaa3949906322c97/>
+<csr-id-9a2f7cd55c05f2fdb0ae62f0efca9dfa451694c7/>
+<csr-id-5034544b36994177009ccc8d6c07cb000b429174/>
+<csr-id-e701e7e9cc571108ca210fc0ca23494d6a1c7208/>
+<csr-id-68ed6d7e2cabc3d3bc78a29003863cd4194549fa/>
+<csr-id-42acc88bbc63850c0d38db70bc46b1058875e2a0/>
+<csr-id-8e17534b0efa7418eabdc36f89bab9f9db7b2c38/>
+<csr-id-0fd57c6b491a3c8d0127bc1d1f0eb958437edff9/>
+<csr-id-e05c1fefeed23dbedf0420e04a2a408510775380/>
+<csr-id-b1c40b0364ef092cd52d03b34f491b254816b18d/>
+<csr-id-517677147f1c17304c62cf97a1dd09f232ebf5db/>
+<csr-id-f6a6a499f20e12e2bcca734bdf3c8599d37f6a6f/>
+<csr-id-39778fd76191cfdb60df87eab8da59e575e48c78/>
+
+ - <csr-id-d59d362f12bf617656bae80596120c8bf823b090/> add and implement various new http options for the `curl` backend.
+ - `schannel_check_revoke` as `curl`-backend specific configuration.
+
+### Chore
+
+ - <csr-id-f7f136dbe4f86e7dee1d54835c420ec07c96cd78/> uniformize deny attributes
+ - <csr-id-533e887e80c5f7ede8392884562e1c5ba56fb9a8/> remove default link to cargo doc everywhere
+
+### Changed
+
+ - <csr-id-28615b3bb9acff86d7a5520172513e3cc22aeda1/> `TransportV2Ext::invoke(…,features,…)` can take key-value pairs more flexibly.
+ Value can now also be owned, which is useful if the value type is a
+ `Cow<'_, String>`.
+
+### Documentation
+
+ - <csr-id-39ed9eda62b7718d5109135e5ad406fb1fe2978c/> fix typos
+
+### Bug Fixes
+
+<csr-id-5f2276b63129163096be3cb229864fc589348da8/>
+<csr-id-ff0332e815c228cc5cdfe58c3598ad261bb2879e/>
+<csr-id-85dcda81d3fec03ad5687b0e0329cefedd925722/>
+<csr-id-0d0eb4aa46b265f97ada7b54d8bcc29decc42e50/>
+<csr-id-4927adf1a57166b581fc293a33f84ef628af70db/>
+<csr-id-7ab7c2409a47fae587531c0c3b203cd646e32984/>
+<csr-id-b0083e38c82829b4d8b81542fc8d1025089e2869/>
+<csr-id-375051fa97d79f95fa7179b536e616c4aefd88e2/>
+<csr-id-4b5d6dfb58f325bba692e1e32636c24ba058022f/>
+<csr-id-41b0c19e1aca9406015932862058756af2a26dda/>
+<csr-id-6d8b66a9bee901eb8cb869e6e28dbb25988f1fed/>
+<csr-id-237682a529dc54e33e4738f34915d872aeb89514/>
+<csr-id-5220f9a59fb699e111342b076145a6899d36d433/>
+
+ - <csr-id-eff7ad79d8b920ab9d936d6268060cfc8ae1b47a/> make clear in docs that the writer needs to be dropped for good measure.
+ Otherwise, some transports might deadlock.
+ - <csr-id-acb4c170395779d1c34d74951121acd5c5b19c65/> Use single quotes for ssh path arg
+ Git uses this method of quoting args for SSH transport too
+ Some Git SSH servers require this method to be used (eg. BitBucket)
+ - <csr-id-85c33825678bddde5a3fe409bbb1ff5cecd3a0fc/> Place port for PuTTY and derivates in separate argument
+ All three PuTTY clients require the port to be separated from the "-P"
+ argument
+ - <csr-id-6ba799c9d6b17ed665d3c352c3c4bb35c9f771bb/> `gix clone ssh://...` won't deadlock anymore.
+ For `cargo` specifically we now parse stderr to see if permission errors
+ occour. This links stderr and stdout and we have to pass information from
+ a supervisor thread that parses stderr to stdout and use the information to
+ return a custom io error in time.
+ Now the algorithm is adjusted to never be able to deadlock, as the problem
+ is inherently racy and somewhat hard to implement it properly especially without
+ a good test suite built-into `gitoxde` - there are no ssh servers one can easily
+ spin up cross-platform.
+ - <csr-id-ec2f2e31a714334bc0942eab08d306d4e0952933/> file:// command invocation won't spill stderr output.
+ This usually doesn't add any benefit to the user as we might see
+ events like the git process' failure to flush to a closed channel
+ even though this is entirely handled by the Rust side of things.
+
+ I can imagine that one day this might become a configurable to help
+ with debugging to help making better-behaved clients, but maybe it
+ won't ever matter once the default file:// transport is built-in and
+ native.
+ - <csr-id-fed38c90df546c4bfc57ef66c92b4c9312c90586/> improve error message for when an invoked transport program can't be found.
+ - <csr-id-f0997bfab2ba66fb12b0c9d4d673faeabda9687c/> assure processing thread is up before continuing.
+ That way one may hope that we never leave stderr output unprocessed.
+ - <csr-id-923278b4f245c31245a83f5f4d6e3b7dce8134e2/> port selections for SSH urls are now respected for protocol V1 as well.
+ - <csr-id-0ff127c62c2cc47b93ef4af108a382c62af1d3fb/> propery adjust `host` argument for `ssh` program to include a user name.
+ Otherwise it would not use a user at all which then defaults to the currently logged
+ in user, something that typically won't work with servers that demand `git`.
+ - <csr-id-c62e5c7d415351aefafeb75f0ab926c7c45c6ede/> fixes SSH clone from scp-like/relatives URLs
+ - Removes git-upload-pack extra parameters (rejected by both github and gitlab)
+
+### Commit Statistics
+
+<csr-read-only-do-not-edit/>
+
+ - 736 commits contributed to the release over the course of 967 calendar days.
+ - 61 commits were understood as [conventional](https://www.conventionalcommits.org).
+ - 14 unique issues were worked on: [#198](https://github.com/Byron/gitoxide/issues/198), [#200](https://github.com/Byron/gitoxide/issues/200), [#222](https://github.com/Byron/gitoxide/issues/222), [#254](https://github.com/Byron/gitoxide/issues/254), [#279](https://github.com/Byron/gitoxide/issues/279), [#301](https://github.com/Byron/gitoxide/issues/301), [#329](https://github.com/Byron/gitoxide/issues/329), [#386](https://github.com/Byron/gitoxide/issues/386), [#450](https://github.com/Byron/gitoxide/issues/450), [#470](https://github.com/Byron/gitoxide/issues/470), [#602](https://github.com/Byron/gitoxide/issues/602), [#639](https://github.com/Byron/gitoxide/issues/639), [#691](https://github.com/Byron/gitoxide/issues/691), [#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 26 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))
+ - Respect release-wide ignore list to allow removing entire conventional headlines ([`145103d`](https://github.com/Byron/gitoxide/commit/145103d4aa715386da9d4953f7f85fadc49fff9a))
+ - 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))
+ * **[#200](https://github.com/Byron/gitoxide/issues/200)**
+ - Feat: Lift io::Errors to response::Error::UploadPack(…)… ([`f293b63`](https://github.com/Byron/gitoxide/commit/f293b633d16c0f7393d0ede64e12f14e47d0296b))
+ * **[#222](https://github.com/Byron/gitoxide/issues/222)**
+ - Update changelogs prior to release ([`9a493d0`](https://github.com/Byron/gitoxide/commit/9a493d0651b0b6d71cf230dc510a658be7f8cb19))
+ * **[#254](https://github.com/Byron/gitoxide/issues/254)**
+ - Adjust changelogs prior to git-pack release ([`6776a3f`](https://github.com/Byron/gitoxide/commit/6776a3ff9fa5a283da06c9ec5723d13023a0b267))
+ * **[#279](https://github.com/Byron/gitoxide/issues/279)**
+ - Adjust to changes in git-hash and git-pack ([`0cae25b`](https://github.com/Byron/gitoxide/commit/0cae25b1bb3c902ec323f17a1d9743e42fe213d0))
+ * **[#301](https://github.com/Byron/gitoxide/issues/301)**
+ - Update changelogs prior to release ([`84cb256`](https://github.com/Byron/gitoxide/commit/84cb25614a5fcddff297c1713eba4efbb6ff1596))
+ * **[#329](https://github.com/Byron/gitoxide/issues/329)**
+ - In-manifest and in-lib documentation of feature toggles ([`39778fd`](https://github.com/Byron/gitoxide/commit/39778fd76191cfdb60df87eab8da59e575e48c78))
+ - Document all features related to serde1 ([`72b97f2`](https://github.com/Byron/gitoxide/commit/72b97f2ae4dc7642b160f183c6d5df4502dc186f))
+ * **[#386](https://github.com/Byron/gitoxide/issues/386)**
+ - Adapt to changes in git-sec ([`c5e2346`](https://github.com/Byron/gitoxide/commit/c5e2346cee53019b1b321e45cf080b210e60bb7a))
+ - Use `git-credentials` in `git-protocol` ([`32dc182`](https://github.com/Byron/gitoxide/commit/32dc1829a5661f66396d109c8d0a8eaae6b1f532))
+ * **[#450](https://github.com/Byron/gitoxide/issues/450)**
+ - `client::Capabilities` lifetimes now point to `'a` instead of `'self`. ([`4b5d6df`](https://github.com/Byron/gitoxide/commit/4b5d6dfb58f325bba692e1e32636c24ba058022f))
+ - Set the protocol version for local git transports as well. ([`41b0c19`](https://github.com/Byron/gitoxide/commit/41b0c19e1aca9406015932862058756af2a26dda))
+ - Remove `Drop` for `SpawnProcessOnDemand`. ([`6d8b66a`](https://github.com/Byron/gitoxide/commit/6d8b66a9bee901eb8cb869e6e28dbb25988f1fed))
+ - Allow defaulting `client::Capabilities`. ([`e05c1fe`](https://github.com/Byron/gitoxide/commit/e05c1fefeed23dbedf0420e04a2a408510775380))
+ - Use `&dyn Any` instead of unspecified serialization format, as it's the right way. ([`779eefe`](https://github.com/Byron/gitoxide/commit/779eefed97685300f4cd7b09957d3442c96e5b1f))
+ - Add `fetch::Transport::configure` to generically configure any transport. ([`78ad3df`](https://github.com/Byron/gitoxide/commit/78ad3df64f2c016ba17b158bd9ab1d2341aab399))
+ - `client::http::connect()` returns `Transport<Impl>` directly. ([`1cf66c4`](https://github.com/Byron/gitoxide/commit/1cf66c4dbc7a0404701efe4335363c2636ce32f8))
+ - Revert "FAIL: try to make the transport configurable after being boxed, but…" ([`fbb96e4`](https://github.com/Byron/gitoxide/commit/fbb96e4d55e322243bf5500605f72e93b103e308))
+ - Try to make the transport configurable after being boxed, but… ([`5bef0a0`](https://github.com/Byron/gitoxide/commit/5bef0a00e8d01110c054a517f6d9696f981a7efc))
+ - Option to print server information about the connection ([`4720666`](https://github.com/Byron/gitoxide/commit/4720666c8bfdaa3acc5c832b44755d4b4f86e16e))
+ - Upgrade `bstr` to `1.0.1` ([`99905ba`](https://github.com/Byron/gitoxide/commit/99905bacace8aed42b16d43f0f04cae996cb971c))
+ - Adjust to changes in `git-url` ([`bb83843`](https://github.com/Byron/gitoxide/commit/bb838430ecbaa18921ef60af04ff684809572ce2))
+ - Make async `conenct()` signature compatible with the blocking implementation. ([`5220f9a`](https://github.com/Byron/gitoxide/commit/5220f9a59fb699e111342b076145a6899d36d433))
+ - Sketch of simple delegate to collect listed refs ([`1c5f561`](https://github.com/Byron/gitoxide/commit/1c5f5617940efe818a5e2aca5afe2cbd7f4ad940))
+ - Adjust to changes in `git-transport` ([`94a623b`](https://github.com/Byron/gitoxide/commit/94a623b57e8fa2876731f74224b406ac56838edc))
+ - Fix async `connect()` to match new signature ([`eb15a79`](https://github.com/Byron/gitoxide/commit/eb15a7971f864bfe02073a21d545ce9ac3dc58e5))
+ - `client::connect()` supports anything that parses into a `git_url::Url`; turn http url back to &str ([`9509ce4`](https://github.com/Byron/gitoxide/commit/9509ce4faeca8b4e1527bac625370403495bb03c))
+ - `client::connect()` now takes a `&BStr` as URL ([`52e8c14`](https://github.com/Byron/gitoxide/commit/52e8c149ff17ce894cc30d03ead1988f52f0663e))
+ - `connect()` method is available in when `async-std` feature is set along with `async-client` ([`f6a6a49`](https://github.com/Byron/gitoxide/commit/f6a6a499f20e12e2bcca734bdf3c8599d37f6a6f))
+ - Use `thiserror` instead of `quickerror` ([`71a43d0`](https://github.com/Byron/gitoxide/commit/71a43d0bc12661efcb9c94697c704f700a3be488))
+ - Better docs for `git-transport` ([`30b7b67`](https://github.com/Byron/gitoxide/commit/30b7b67740f80b9954c7fe77d7007722cb95d673))
+ - Adapt to changes in `git-url` ([`9456146`](https://github.com/Byron/gitoxide/commit/9456146531226e5efc6e1e4e2e89b03683f8a422))
+ - Adapt to changes in `git-url` and use `BString` to represent URLs. ([`12589cc`](https://github.com/Byron/gitoxide/commit/12589cc6f08e4d7aabae30bcdadaa0c2b4850229))
+ * **[#470](https://github.com/Byron/gitoxide/issues/470)**
+ - Update changelogs prior to release ([`caa7a1b`](https://github.com/Byron/gitoxide/commit/caa7a1bdef74d7d3166a7e38127a59f5ab3cfbdd))
+ * **[#602](https://github.com/Byron/gitoxide/issues/602)**
+ - Improve compile-time errors if mutually exclusive http-client features are set. ([`b0083e3`](https://github.com/Byron/gitoxide/commit/b0083e38c82829b4d8b81542fc8d1025089e2869))
+ - `max-pure` now builds without any C build tooling due to lack of `openssl-sys`. ([`4308a20`](https://github.com/Byron/gitoxide/commit/4308a209dddcbb461c34d45fb9af8b4621d4600a))
+ * **[#639](https://github.com/Byron/gitoxide/issues/639)**
+ - Correctly display what's actual and expected when failing to parse capabilities. ([`7ab7c24`](https://github.com/Byron/gitoxide/commit/7ab7c2409a47fae587531c0c3b203cd646e32984))
+ * **[#691](https://github.com/Byron/gitoxide/issues/691)**
+ - Set `rust-version` to 1.64 ([`55066ce`](https://github.com/Byron/gitoxide/commit/55066ce5fd71209abb5d84da2998b903504584bb))
+ * **[#XXX](https://github.com/Byron/gitoxide/issues/XXX)**
+ - Prepare changelogs prior to release ([`8c0bca3`](https://github.com/Byron/gitoxide/commit/8c0bca37ff9fbaadbe55561fb2b0d649980c95b1))
+ * **Uncategorized**
+ - Release 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 ([`7fc00f8`](https://github.com/Byron/gitoxide/commit/7fc00f87d74aedf631ce4032be1cdfe1804c7e7d))
+ - 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))
+ - Merge branch 'rename-crates' into inform-about-gix-rename ([`c9275b9`](https://github.com/Byron/gitoxide/commit/c9275b99ea43949306d93775d9d78c98fb86cfb1))
+ - Fix git-transport tests ([`1727fe9`](https://github.com/Byron/gitoxide/commit/1727fe9e64597a02e231b84fb94a35ed6ed37a50))
+ - 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))
+ - 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-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))
+ - Rename `git-transport` to `gix-transport` ([`8c353ea`](https://github.com/Byron/gitoxide/commit/8c353ea00c805604113a567d2f5157be94cc9f28))
+ - 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-worktree` to `gix-worktree` ([`108bb1a`](https://github.com/Byron/gitoxide/commit/108bb1a634f4828853fb590e9fc125f79441dd38))
+ - Adjust to renamining of `git-tui` to `gix-tui` ([`8782262`](https://github.com/Byron/gitoxide/commit/878226282500993e755cdf99bf0076f91130e0cd))
+ - 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))
+ - Fix typos ([`39ed9ed`](https://github.com/Byron/gitoxide/commit/39ed9eda62b7718d5109135e5ad406fb1fe2978c))
+ - Make clear in docs that the writer needs to be dropped for good measure. ([`eff7ad7`](https://github.com/Byron/gitoxide/commit/eff7ad79d8b920ab9d936d6268060cfc8ae1b47a))
+ - 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))
+ - Improve documentation on http options ([`5bf9547`](https://github.com/Byron/gitoxide/commit/5bf954717f5ff7c74783c2e459ebb5134d470583))
+ - Merge branch 'ssh-quoting' ([`cc35025`](https://github.com/Byron/gitoxide/commit/cc350250d4ee6800c8033891074389455b115072))
+ - Refactor ([`deed1f1`](https://github.com/Byron/gitoxide/commit/deed1f1a81669c53475a88a504f593884d179363))
+ - Use single quotes for ssh path arg ([`acb4c17`](https://github.com/Byron/gitoxide/commit/acb4c170395779d1c34d74951121acd5c5b19c65))
+ - Merge pull request #712 from exactly-one-kas/putty-port ([`021248c`](https://github.com/Byron/gitoxide/commit/021248c8c4784df8f9fa179269f8e6b036d2ddcc))
+ - Place port for PuTTY and derivates in separate argument ([`85c3382`](https://github.com/Byron/gitoxide/commit/85c33825678bddde5a3fe409bbb1ff5cecd3a0fc))
+ - 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))
+ - Upgrade base64 ([`b5408ba`](https://github.com/Byron/gitoxide/commit/b5408baa4798ba12d7e6450f6f0aa0f630983f0c))
+ - Export `PostBodyDataKind` ([`7fa757f`](https://github.com/Byron/gitoxide/commit/7fa757ff3b9aa620b1e68fb9710da687ff90cd21))
+ - 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))
+ - Merge branch 'gix-clone-improvements' ([`76c99f3`](https://github.com/Byron/gitoxide/commit/76c99f3005f1b0031921b536f5d268715e41f3c8))
+ - `gix clone ssh://...` won't deadlock anymore. ([`6ba799c`](https://github.com/Byron/gitoxide/commit/6ba799c9d6b17ed665d3c352c3c4bb35c9f771bb))
+ - Release git-transport v0.25.1 ([`e0b12fe`](https://github.com/Byron/gitoxide/commit/e0b12fe64b50a1b614111924b55ce02f1c39ac00))
+ - File:// command invocation won't spill stderr output. ([`ec2f2e3`](https://github.com/Byron/gitoxide/commit/ec2f2e31a714334bc0942eab08d306d4e0952933))
+ - 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))
+ - Improve error message for when an invoked transport program can't be found. ([`fed38c9`](https://github.com/Byron/gitoxide/commit/fed38c90df546c4bfc57ef66c92b4c9312c90586))
+ - Assure processing thread is up before continuing. ([`f0997bf`](https://github.com/Byron/gitoxide/commit/f0997bfab2ba66fb12b0c9d4d673faeabda9687c))
+ - Parse additional ssh messaegs into io-errors. ([`c60c89d`](https://github.com/Byron/gitoxide/commit/c60c89dcede9590f55246495a159a614426402d7))
+ - Make fmt ([`511ed00`](https://github.com/Byron/gitoxide/commit/511ed0000397a5b268530c8f5362e7d25b7c1594))
+ - Merge branch 'adjustments-for-cargo' ([`f8c562a`](https://github.com/Byron/gitoxide/commit/f8c562a559e6dc3377583cc7200585dad7c3d481))
+ - Ptions for `client::connect()` and support for more than one ssh variant, including permission-denied detection. ([`6fa2764`](https://github.com/Byron/gitoxide/commit/6fa27642aa57613cae82ae680f02923dad25d474))
+ - Port selections for SSH urls are now respected for protocol V1 as well. ([`923278b`](https://github.com/Byron/gitoxide/commit/923278b4f245c31245a83f5f4d6e3b7dce8134e2))
+ - Propery adjust `host` argument for `ssh` program to include a user name. ([`0ff127c`](https://github.com/Byron/gitoxide/commit/0ff127c62c2cc47b93ef4af108a382c62af1d3fb))
+ - 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))
+ - Merge branch 'adjustments-for-cargo' ([`d821fc5`](https://github.com/Byron/gitoxide/commit/d821fc5b4ef4ba606f2b6bb68b66f7260a0205dc))
+ - Add and implement various new http options for the `curl` backend. ([`d59d362`](https://github.com/Byron/gitoxide/commit/d59d362f12bf617656bae80596120c8bf823b090))
+ - Merge branch 'fix/ssh-clone' ([`3678a6a`](https://github.com/Byron/gitoxide/commit/3678a6abab6f59ff7008ccfe02bb8d61da47e166))
+ - Refactor ([`24070a3`](https://github.com/Byron/gitoxide/commit/24070a3b970554d5d4815e87bcbfb6fb5524a006))
+ - Fixes SSH clone from scp-like/relatives URLs ([`c62e5c7`](https://github.com/Byron/gitoxide/commit/c62e5c7d415351aefafeb75f0ab926c7c45c6ede))
+ - Release git-url v0.12.1, git-transport v0.24.1, git-protocol v0.25.1, git-repository v0.30.1, git-commitgraph v0.12.0, gitoxide-core v0.22.0, gitoxide v0.20.0 ([`08ec3a9`](https://github.com/Byron/gitoxide/commit/08ec3a93d77a1018439a5c41c23729ffed27c5a5))
+ - Prepare changelogs prior to release ([`68ce15d`](https://github.com/Byron/gitoxide/commit/68ce15d07b50cfacdac0d1e42fe7f5e6330ba523))
+ - Merge branch 'adjustments-for-cargo' ([`5afa7f5`](https://github.com/Byron/gitoxide/commit/5afa7f51342deaf0938e7fb2ebe6a578e83ab645))
+ - 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))
+ - Make streaming otional for any reqwest. ([`041eca5`](https://github.com/Byron/gitoxide/commit/041eca547a6629c8540728eba95dbcd636285ba9))
+ - Don't enforce V2 as protocol, but smoothly downgrade like git does. ([`5f2276b`](https://github.com/Byron/gitoxide/commit/5f2276b63129163096be3cb229864fc589348da8))
+ - Merge branch 'adjustments-for-cargo' ([`94750e1`](https://github.com/Byron/gitoxide/commit/94750e15831969059551af35d31c21009462084d))
+ - Improve granularity of IO errors for `curl` backends. ([`0a2b135`](https://github.com/Byron/gitoxide/commit/0a2b135d19ce1f1b4b0394befaa3949906322c97))
+ - Http transports can now reuse a connection. ([`ff0332e`](https://github.com/Byron/gitoxide/commit/ff0332e815c228cc5cdfe58c3598ad261bb2879e))
+ - Merge branch 'adjustments-for-cargo' ([`70ccbb2`](https://github.com/Byron/gitoxide/commit/70ccbb21b1113bdeb20b52d274141a9fdb75f579))
+ - Merge branch 'main' into adjustments-for-cargo ([`bb60d3d`](https://github.com/Byron/gitoxide/commit/bb60d3d5cb9dbd7abe61accded6d21e320c624db))
+ - Adapt to changes in git-repository ([`89230f4`](https://github.com/Byron/gitoxide/commit/89230f4e151056abaa2bce39d9d18f6dd1512d59))
+ - Merge branch 'paulyoung/scheme-ext' ([`3e27550`](https://github.com/Byron/gitoxide/commit/3e27550577ea942427a57c902570f0416f540753))
+ - `IsSpuriousError` trait and its implementation. ([`9a2f7cd`](https://github.com/Byron/gitoxide/commit/9a2f7cd55c05f2fdb0ae62f0efca9dfa451694c7))
+ - Don't pre-configure curl. ([`85dcda8`](https://github.com/Byron/gitoxide/commit/85dcda81d3fec03ad5687b0e0329cefedd925722))
+ - Ssh connection remove '=' in port argument ([`0d0eb4a`](https://github.com/Byron/gitoxide/commit/0d0eb4aa46b265f97ada7b54d8bcc29decc42e50))
+ - Merge branch 'fix-638' ([`eb4c5f0`](https://github.com/Byron/gitoxide/commit/eb4c5f051ae2a4eb7178289cfc1437417f265608))
+ - Make it possible to parse handshakes without newlines in packetlines #(639) ([`4927adf`](https://github.com/Byron/gitoxide/commit/4927adf1a57166b581fc293a33f84ef628af70db))
+ - Merge branch 'fixture-async' ([`eca6705`](https://github.com/Byron/gitoxide/commit/eca670585db212985d653cb2c6ec3636ec560905))
+ - Async version of ref-line parsing now reads line by line. ([`dadd896`](https://github.com/Byron/gitoxide/commit/dadd8964ec551702908055476df10624b266a79f))
+ - Fix hang when reading packetlines lines directly via HTTP ([`2d033ab`](https://github.com/Byron/gitoxide/commit/2d033ab7c7915d27c86a1e7dfbe9cf70d7ae3320))
+ - Merge branch 'remove-lines-parsing' ([`9d8e32d`](https://github.com/Byron/gitoxide/commit/9d8e32d3c276fec34e3fce0feb29de0d24a8d1d2))
+ - Provide support for reading packetlines directly. ([`1204bfc`](https://github.com/Byron/gitoxide/commit/1204bfcaadd31ed198b923df05f19115da3754a4))
+ - `Capabiltiies::from_lines()` takes a single buffer. ([`08dcda2`](https://github.com/Byron/gitoxide/commit/08dcda254bc942dcc36d432cf7130c2ce4c6d54e))
+ - Make fmt ([`747008d`](https://github.com/Byron/gitoxide/commit/747008d9d370844574dda94e5bec1648c4deb57e))
+ - Merge branch 'main' into http-config ([`6b9632e`](https://github.com/Byron/gitoxide/commit/6b9632e16c416841ffff1b767ee7a6c89b421220))
+ - `client::http::Options::no_proxy` to disable a proxy for given hosts. ([`5034544`](https://github.com/Byron/gitoxide/commit/5034544b36994177009ccc8d6c07cb000b429174))
+ - `client::http::Options::verbose` to see more debug output. ([`e701e7e`](https://github.com/Byron/gitoxide/commit/e701e7e9cc571108ca210fc0ca23494d6a1c7208))
+ - Release git-features v0.24.1, git-actor v0.14.1, git-index v0.9.1 ([`7893502`](https://github.com/Byron/gitoxide/commit/789350208efc9d5fc6f9bc4f113f77f9cb445156))
+ - Interpret the FollowRedirects option for the curl HTTP backend. ([`8e158c3`](https://github.com/Byron/gitoxide/commit/8e158c3f4056f59724fe91587157ef0daa517964))
+ - Merge branch 'http-config' ([`a4ff140`](https://github.com/Byron/gitoxide/commit/a4ff140a0d3607cf282c49228c1248bd36d464fd))
+ - Merge branch 'main' into http-config ([`bcd9654`](https://github.com/Byron/gitoxide/commit/bcd9654e56169799eb706646da6ee1f4ef2021a9))
+ - Make fmt ([`0abab7d`](https://github.com/Byron/gitoxide/commit/0abab7da2ec1b8560e6c1eb009f534c9fc7814fe))
+ - 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 'max-pure' ([`03ff188`](https://github.com/Byron/gitoxide/commit/03ff1882f2982fba38fbbf245eea13ef9df50f33))
+ - Thanks clippy ([`c7cba33`](https://github.com/Byron/gitoxide/commit/c7cba333dd8654995b367498609b4280fe394402))
+ - 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))
+ - Curl can authenticate the proxy now and store or reject credentials. ([`63b9050`](https://github.com/Byron/gitoxide/commit/63b9050240b80c5493dab3e8d0b1c675f83d78d6))
+ - Support for proxy authentication in http configuration. ([`68ed6d7`](https://github.com/Byron/gitoxide/commit/68ed6d7e2cabc3d3bc78a29003863cd4194549fa))
+ - Setup curl proxy authentication method ([`c048990`](https://github.com/Byron/gitoxide/commit/c0489900f4ec385c9033c585c16934ff54941d65))
+ - Support for reading `http.proxyAuthMethod` ([`92f88c9`](https://github.com/Byron/gitoxide/commit/92f88c94ff288b5675ca3296c27ffb66e1716c22))
+ - Release git-glob v0.4.2, git-config-value v0.8.2, git-lock v2.2.0, git-ref v0.19.0, git-config v0.11.0, git-discover v0.8.0, git-index v0.8.0, git-transport v0.22.0, git-protocol v0.23.0, git-worktree v0.8.0, git-repository v0.28.0, gitoxide-core v0.20.0, gitoxide v0.18.0, safety bump 9 crates ([`0c253b1`](https://github.com/Byron/gitoxide/commit/0c253b15143dcedfe4c66d64ab1ea6e097030651))
+ - Prepare changelogs prior to release ([`fe5721f`](https://github.com/Byron/gitoxide/commit/fe5721f888c64c79fe9a734a9e33b94a282f8d97))
+ - Merge branch 'http-config' ([`665b53e`](https://github.com/Byron/gitoxide/commit/665b53e1c2e1de65fafa28b669f58977868bbc81))
+ - Merge branch 'push-support' ([`42356ab`](https://github.com/Byron/gitoxide/commit/42356abf9d08dd86ce464fa48e25bbcc98ceefd4))
+ - Add test to see if we can correctly read the server response. ([`4d84a20`](https://github.com/Byron/gitoxide/commit/4d84a208e4d0a8bf3dbcfa6912b92ec1bdf4ec05))
+ - `client::RequestWriter::into_parts()` to obtain a bare write handled along with a buf reader with packetline capabilties. ([`42acc88`](https://github.com/Byron/gitoxide/commit/42acc88bbc63850c0d38db70bc46b1058875e2a0))
+ - Add simple push request/response as obtained through HTTP proxying ([`0573107`](https://github.com/Byron/gitoxide/commit/05731070a5cfb403bb82d8d8baa360ffe8cc45a9))
+ - Avoid hardcoding some arbitrary default for connect timeouts, use curl default instead like git ([`1766568`](https://github.com/Byron/gitoxide/commit/17665683efc8e25cbb35737a8c4132b98e5b6b7a))
+ - Set TCP keepalive just like `git` does ([`ee0276c`](https://github.com/Byron/gitoxide/commit/ee0276c7659122e32851da8ee6a9e663982bcda5))
+ - Set curl `proxy-type` similar to how git does it ([`717b09f`](https://github.com/Byron/gitoxide/commit/717b09fb3cac024b85a885e253c52e1f37bc0590))
+ - Merge branch 'main' into http-config ([`f4ff821`](https://github.com/Byron/gitoxide/commit/f4ff821fd4233dd1dc1a449af4d4600becf3b4ac))
+ - Merge branch 'async-fetch' ([`0c9c48b`](https://github.com/Byron/gitoxide/commit/0c9c48b3b91a1396eb1796f288a2cb10380d1f14))
+ - Fix docs ([`0d1a00d`](https://github.com/Byron/gitoxide/commit/0d1a00da4f4e84f989f18d16878cc95ace797b68))
+ - Add `Debug` to `client::http::Options` ([`375565f`](https://github.com/Byron/gitoxide/commit/375565f20906b4ca373978b89573cf711c3637a9))
+ - `client::TransportWithoutIO::to_url()` returns `Cow<'_, BStr>`. ([`07512db`](https://github.com/Byron/gitoxide/commit/07512db093e62d9b9185368bd3fa561cfcd1d1d2))
+ - `client::TransportWithoutIO::to_url()` returns `BString`. ([`fe2042b`](https://github.com/Byron/gitoxide/commit/fe2042bff9ae38bf76b76cef14986f9f76bded7d))
+ - Add missing `Debug` impl on transport option types ([`7ca4dec`](https://github.com/Byron/gitoxide/commit/7ca4dec2df83ce7763383fb93db5ba0001c2cc27))
+ - Fix build ([`b9a5eea`](https://github.com/Byron/gitoxide/commit/b9a5eeafb8e86d7f8faaa4ce1884487db24e554d))
+ - `client::TransportV2Ext::invoke()` supports owned `capabilities`. ([`759b5d4`](https://github.com/Byron/gitoxide/commit/759b5d482de048deb24d14043a173079914e7ac8))
+ - `TransportV2Ext::invoke(…,features,…)` can take key-value pairs more flexibly. ([`28615b3`](https://github.com/Byron/gitoxide/commit/28615b3bb9acff86d7a5520172513e3cc22aeda1))
+ - Explain how the `user_agent` option is going to work on the transport layer ([`328c069`](https://github.com/Byron/gitoxide/commit/328c0695692ebde5999c941b95c4cd330edb0f04))
+ - Implement all straightforward curl options, which includes basic proxy settings. ([`0b60097`](https://github.com/Byron/gitoxide/commit/0b60097671fe2d8037fe44271678fe380ecbd543))
+ - Add all fields we'd like to implement for the curl transport. ([`cfc1b9c`](https://github.com/Byron/gitoxide/commit/cfc1b9ccea32b2f870427014543196f09cbae9ac))
+ - A note about the status of the http `reqwest` backend ([`4724604`](https://github.com/Byron/gitoxide/commit/47246047dc9c476149511086612411d39b257bc6))
+ - Remove double-arc construct in `reqwest` backend ([`c6a474c`](https://github.com/Byron/gitoxide/commit/c6a474c9b22e969b040e7ce05f6fba245dc408d0))
+ - Sketch of reqwest specific backends can be handled ([`a8b3f96`](https://github.com/Byron/gitoxide/commit/a8b3f96110dc9a68c680bad07c52b0204bd539a3))
+ - A sketch of passing curl options down to curl… ([`63e24fc`](https://github.com/Byron/gitoxide/commit/63e24fcb0ad66a9fd3de1e4e440bbdbc430fc614))
+ - 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))
+ - Fix build ([`910d665`](https://github.com/Byron/gitoxide/commit/910d665e66d6e737a7bab3598c0c0ebfdda1a9cc))
+ - Thanks clippy ([`0dc4c6f`](https://github.com/Byron/gitoxide/commit/0dc4c6fb2199514437acd7629ea2a4c6bc0555c5))
+ - Move HTTP service checks up to the HTTP layer (blocking & async) ([`3046646`](https://github.com/Byron/gitoxide/commit/3046646a2a0b06a0ca5c2f28b99b9a2b10c73368))
+ - Fix fixture to work with test setup. ([`cf9a53b`](https://github.com/Byron/gitoxide/commit/cf9a53b8bc7265386d3336cff791462011b42d61))
+ - Fix error when no service annoucment is sent by the server ([`a1d876f`](https://github.com/Byron/gitoxide/commit/a1d876f8474a05aeca2d852ee126f0d0e110c0f9))
+ - 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 'git_protocol_host' ([`d13c590`](https://github.com/Byron/gitoxide/commit/d13c59070ae6f1661dd6fee056cef1ff75c89222))
+ - Avoid invalid invocations of `git` by removing `GIT_CONFIG_COUNT` ([`aa315b4`](https://github.com/Byron/gitoxide/commit/aa315b4a136bec57a1cce4b245c606904adc5c12))
+ - Fix connections over the git:// protocol not specifying the host ([`8f985d4`](https://github.com/Byron/gitoxide/commit/8f985d44c71a4434c4cf4ae0e360480bc3694a47))
+ - Merge branch 'main' into write-sparse-index ([`c4e6849`](https://github.com/Byron/gitoxide/commit/c4e68496c368611ebe17c6693d06c8147c28c717))
+ - Make fmt ([`ea2136b`](https://github.com/Byron/gitoxide/commit/ea2136b065979cecb3a1fdbf7b20ed7514128d9a))
+ - Merge branch 'gix-clone' ([`def53b3`](https://github.com/Byron/gitoxide/commit/def53b36c3dec26fa78939ab0584fe4ff930909c))
+ - Merge branch 'main' into gix-clone ([`fa27570`](https://github.com/Byron/gitoxide/commit/fa27570f491388cce6137af44330d76870d07202))
+ - Merge branch 'main' into write-sparse-index ([`70963f5`](https://github.com/Byron/gitoxide/commit/70963f5d8e3b59ce6fe8bcc1844218ac717f3390))
+ - Merge branch 'paulyoung/reqwest-body' ([`c9c1658`](https://github.com/Byron/gitoxide/commit/c9c1658d6afa6edca60d8072a23c7f4899891af1))
+ - Merge branch 'main' into gix-clone ([`64f81d7`](https://github.com/Byron/gitoxide/commit/64f81d78ae75a0e5914f431bbdc385a6d40f8835))
+ - Ensure body can be read before configure_request ([`9065a88`](https://github.com/Byron/gitoxide/commit/9065a8824ea80891c4c06ea28e0780e5517a353d))
+ - Merge branch 'paulyoung/reqwest-configuration' ([`93f2dd8`](https://github.com/Byron/gitoxide/commit/93f2dd8f7db87afc04a523458faaa46f9b33f21a))
+ - Turn unwrap into an explicit expectation ([`57d9078`](https://github.com/Byron/gitoxide/commit/57d90783edbb20f9c7fac782e9cb90943d4941d7))
+ - Allow `configure_request` to return an error as well. ([`f5e14c3`](https://github.com/Byron/gitoxide/commit/f5e14c3aead5776962c380852e93352364d637e8))
+ - Remove unused field ([`8a0a5d6`](https://github.com/Byron/gitoxide/commit/8a0a5d6262cc51874fa1d35e40b901333945117c))
+ - Configure Request instead of ClientBuilder ([`0885e6e`](https://github.com/Byron/gitoxide/commit/0885e6e5882a133a55d43fc453056564752028ad))
+ - Resolve issues with configure function ([`9124583`](https://github.com/Byron/gitoxide/commit/9124583ebaa650e0344598b277a5b529827776c7))
+ - Sketch out configuring a reqwest client builder ([`74573df`](https://github.com/Byron/gitoxide/commit/74573dfa0c40e4ad3515e680e5699aa760cac38d))
+ - Merge branch 'main' into gix-clone ([`de4fe06`](https://github.com/Byron/gitoxide/commit/de4fe06202906ea5c62e667826b42cf7b57b1ff0))
+ - Allow `client::connect()` to function with `http-client-reqwest` enabled. ([`375051f`](https://github.com/Byron/gitoxide/commit/375051fa97d79f95fa7179b536e616c4aefd88e2))
+ - Refactor ([`d3c6ae1`](https://github.com/Byron/gitoxide/commit/d3c6ae1fa9e1d6a57f32b5831411707fc4cf962e))
+ - Fix formatting ([`a962eb5`](https://github.com/Byron/gitoxide/commit/a962eb5c263de3730b464a0e89615a04caac11e4))
+ - Fix "http not compiled in" w/ http-client-reqwest ([`dff418d`](https://github.com/Byron/gitoxide/commit/dff418dcf2a970929a61c5eea9b4712437582fbc))
+ - 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 'fix-smart-release' ([`aa80b60`](https://github.com/Byron/gitoxide/commit/aa80b606e5570f327660cca42ea81581a6e9d5e3))
+ - Make fmt ([`7b9c065`](https://github.com/Byron/gitoxide/commit/7b9c06547b75929e3e5bf4240f43c7e9bc7d54e0))
+ - `reqwest` as blocking HTTP backend via `http-client-reqwest` feature toggle. ([`8e17534`](https://github.com/Byron/gitoxide/commit/8e17534b0efa7418eabdc36f89bab9f9db7b2c38))
+ - Re-enable last remaining test for reqwest ([`9ef6a54`](https://github.com/Byron/gitoxide/commit/9ef6a546cac78d335bbe60853046bb23becab623))
+ - Merge branch 'main' into new-http-impl ([`702a161`](https://github.com/Byron/gitoxide/commit/702a161ef11fc959611bf44b70e9ffe04561c7ad))
+ - And another previously hanging test works - content-length is critical ([`0b0ba03`](https://github.com/Byron/gitoxide/commit/0b0ba03b42a2143787a2d81d287dcc9ca9adb078))
+ - Clone_v1 now works in reqwest, because… ([`63d5a01`](https://github.com/Byron/gitoxide/commit/63d5a0192ed79f99756930cc9bc9f163119b7a00))
+ - Make fmt ([`53acf25`](https://github.com/Byron/gitoxide/commit/53acf2565743eff7cead7a42011107b2fc8d7e0e))
+ - Merge branch 'fetch-pack' ([`f47c891`](https://github.com/Byron/gitoxide/commit/f47c89129732bcb06fe76a4696fe38ab1151fb0c))
+ - Merge branch 'fetch-pack' ([`3c49400`](https://github.com/Byron/gitoxide/commit/3c49400809c7c2120f4ce704c19a0421545b5acd))
+ - Merge branch 'main' into fetch-pack ([`93917cb`](https://github.com/Byron/gitoxide/commit/93917cb6ecbb30daf3d20bb5a7c65e12211f084f))
+ - Shutdown the stream of the mock server for good measure ([`4e415a8`](https://github.com/Byron/gitoxide/commit/4e415a8414ad0ce2d79bfe29d63d46efa3833218))
+ - Minor fix to test, but now it hangs. 3 hanging to go… ([`164f5d3`](https://github.com/Byron/gitoxide/commit/164f5d3b0c31afe16ad453e5534284a8e6700c84))
+ - One more test works - strangely enough this one doesn't hang. ([`c3150dd`](https://github.com/Byron/gitoxide/commit/c3150dd05bdc158e24adb21d2e678e4008b07740))
+ - Adjust test expectations to pass one part, but now it hangs like clone_v1 ([`4b731ad`](https://github.com/Byron/gitoxide/commit/4b731adf523c00c8d3fd8dcbecd3b4df9efccbe8))
+ - Refactor ([`569c6f2`](https://github.com/Byron/gitoxide/commit/569c6f2cbb0e4c12c1547accae7b3160c80a0caf))
+ - Send error on non-success status codes ([`7ccd4ff`](https://github.com/Byron/gitoxide/commit/7ccd4ffb32d5686d926640ab483381eceb2211de))
+ - Disable all tests failing under reqwest (except for 1); minor fixes to get it to work ([`ee4eb69`](https://github.com/Byron/gitoxide/commit/ee4eb6991ba95983627b14332ccd5e1fd7b43e07))
+ - Properly integrate reqwest to allow testing (even though none of them works yet) ([`250114a`](https://github.com/Byron/gitoxide/commit/250114a856cc9bfeb6f91055a13ca08b6d4b91a7))
+ - Revise drop of header writer to prevent remote hang ([`2981c4d`](https://github.com/Byron/gitoxide/commit/2981c4d36126f837d15fa6f3cac505a0d7fbb0a6))
+ - Revise error handling; better docs to explain the 'protocol' ([`fd9634d`](https://github.com/Byron/gitoxide/commit/fd9634d833536e211d89148f4afc1dca49a385dc))
+ - In theory, that's what's needed for reqwest to work in blocking mode… ([`ce62f5d`](https://github.com/Byron/gitoxide/commit/ce62f5ddcbb454056ec39458e83aa4fb7774c169))
+ - Even more safety when dealing with the remote's error state ([`b63d69b`](https://github.com/Byron/gitoxide/commit/b63d69bdeb8006c0da34bd4f1be8999744b669ad))
+ - Rethink error handling during request creation, completing it ([`1fb0545`](https://github.com/Byron/gitoxide/commit/1fb05451785d2b30124937d3ad53b1e05fbee419))
+ - Frame for filling in the reqwest implementation ([`943fd15`](https://github.com/Byron/gitoxide/commit/943fd153a2daca25c0e716388c528a83ad43be95))
+ - Add 'http-client-reqwest' feature toggle ([`2c9b63c`](https://github.com/Byron/gitoxide/commit/2c9b63cfcc92d4815ddec53d885b6985739f627e))
+ - Compare 'Content-Type' header case-insensitively, as required by the http spec. ([`237682a`](https://github.com/Byron/gitoxide/commit/237682a529dc54e33e4738f34915d872aeb89514))
+ - Merge branch 'paulyoung/git-transport-http-client' ([`c845c16`](https://github.com/Byron/gitoxide/commit/c845c162100ad88f3f7fbc69a5badf80d76d86da))
+ - Add `client::http::Transport::new_http()` constructor. ([`0fd57c6`](https://github.com/Byron/gitoxide/commit/0fd57c6b491a3c8d0127bc1d1f0eb958437edff9))
+ - Introduce http-client feature to decouple from curl ([`31aa2c9`](https://github.com/Byron/gitoxide/commit/31aa2c92ae4d87071bdb436c0995883f53fb355d))
+ - Make Content-Type header check case-insensitive ([`7aa8ab8`](https://github.com/Byron/gitoxide/commit/7aa8ab8b889b278d5f1f49cff570574dfa3146ac))
+ - Merge branch 'fix-git-features' ([`82fd251`](https://github.com/Byron/gitoxide/commit/82fd251ac80d07bc9da8a4d36e517aa35580d188))
+ - 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))
+ - Merge branch 'filter-refs' ([`3773b92`](https://github.com/Byron/gitoxide/commit/3773b92b8372c9a40a74d281149ca65b057a7da9))
+ - Merge branch 'filter-refs' ([`fd14489`](https://github.com/Byron/gitoxide/commit/fd14489f729172d615d0fa1e8dbd605e9eacf69d))
+ - Release git-features v0.22.6 ([`c9eda72`](https://github.com/Byron/gitoxide/commit/c9eda729d8f8bc266c7516c613d38acfb83a4743))
+ - Make fmt ([`535e967`](https://github.com/Byron/gitoxide/commit/535e967666c6da657ff1b7eff7c64ab27cafb182))
+ - 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 index-from-tree ([`bc64b96`](https://github.com/Byron/gitoxide/commit/bc64b96a2ec781c72d1d4daad38aa7fb8b74f99b))
+ - 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))
+ - Merge branch 'remote-ls-refs' ([`39d585d`](https://github.com/Byron/gitoxide/commit/39d585d9f9ac6f3ecf51359c8e37f0a50e21ed45))
+ - 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))
+ - Remove default link to cargo doc everywhere ([`533e887`](https://github.com/Byron/gitoxide/commit/533e887e80c5f7ede8392884562e1c5ba56fb9a8))
+ - 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 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))
+ - Merge branch 'main' into pathspec ([`89ea12b`](https://github.com/Byron/gitoxide/commit/89ea12b558bcc056b892193ee8fb44b8664b5da4))
+ - Merge branch 'main' into cont_include_if ([`daa71c3`](https://github.com/Byron/gitoxide/commit/daa71c3b753c6d76a3d652c29237906b3e28728f))
+ - Thanks clippy ([`e1003d5`](https://github.com/Byron/gitoxide/commit/e1003d5fdee5d4439c0cf0286c67dec9b5e34f53))
+ - 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 git_includeif ([`05eb340`](https://github.com/Byron/gitoxide/commit/05eb34023933918c51c03cf2afd774db89cc5a33))
+ - Merge branch 'main' into msrv-for-windows ([`7cb1972`](https://github.com/Byron/gitoxide/commit/7cb19729133325bdfacedf44cdc0500cbcf36684))
+ - Make fmt ([`251b6df`](https://github.com/Byron/gitoxide/commit/251b6df5dbdda24b7bdc452085f808f3acef69d8))
+ - Merge branch 'main' into repo-status ([`9679d6b`](https://github.com/Byron/gitoxide/commit/9679d6b0e68c28438e22cb65c554d0b31dfaf159))
+ - Merge branch 'git-sec' ([`cd723b5`](https://github.com/Byron/gitoxide/commit/cd723b5ae11148e7e9fd07daf28bc04455d5c46f))
+ - 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))
+ - 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))
+ - Upgrade document-features ([`c35e62e`](https://github.com/Byron/gitoxide/commit/c35e62e0da9ac1f7dcb863f5f9c69108c728d32e))
+ - 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 'sync-db-draft' ([`7d2e20c`](https://github.com/Byron/gitoxide/commit/7d2e20c6fedc2c7e71a307d8d072412fa847a4aa))
+ - Thanks clippy ([`4ca9e07`](https://github.com/Byron/gitoxide/commit/4ca9e07c7ac062d48d64ad7b516274e32dbc51c6))
+ - 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))
+ - 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))
+ - Fix false positive clippy warning. ([`9b8363b`](https://github.com/Byron/gitoxide/commit/9b8363bbf559223a424c2544610b4520bbd37aeb))
+ - 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))
+ - 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))
+ - Merge branch 'main' into changelog-generation ([`c956f33`](https://github.com/Byron/gitoxide/commit/c956f3351d766c748faf0460780e32ac8dfe8165))
+ - Bump git-traverse v0.9.0, safety bump 8 crates ([`d39fabb`](https://github.com/Byron/gitoxide/commit/d39fabb8757369aa19452a457f610fe21dc13a14))
+ - Release git-transport v0.11.1 ([`0952976`](https://github.com/Byron/gitoxide/commit/0952976eac1dac9b8f351ecc9867746b650377f9))
+ - [various #184] configure docs.rs build features ([`cc50249`](https://github.com/Byron/gitoxide/commit/cc502492c512293e93e95610ca80a71896076ded))
+ - Bump git-transport v0.11.0 ([`1149f1b`](https://github.com/Byron/gitoxide/commit/1149f1b716624f8f4fdaed20c803530aebc45599))
+ - [transport #174] prepare for release ([`f8bc517`](https://github.com/Byron/gitoxide/commit/f8bc51763e96d8d0a97d5f367c943441a98c8e95))
+ - Bump git-packetline v0.10.0 ([`b09f391`](https://github.com/Byron/gitoxide/commit/b09f3912e0addd7b4b0ef22bc3a24869d5011646))
+ - [packetline #178] refactor ([`23438fd`](https://github.com/Byron/gitoxide/commit/23438fd4a807376c1d4699732ea6c83c0bde574f))
+ - [packetline #178] rename PacketLine to PacketLineRef… ([`d4c16a9`](https://github.com/Byron/gitoxide/commit/d4c16a93946244177606b58cc702b81a16424ad4))
+ - Release git-transport v0.10.1 ([`dc74d19`](https://github.com/Byron/gitoxide/commit/dc74d1946b89fb42fa8644db31b1fe1a52a56f05))
+ - Apply nightly rustfmt rules. ([`5e0edba`](https://github.com/Byron/gitoxide/commit/5e0edbadb39673d4de640f112fa306349fb11814))
+ - Release git-packetline v0.9.0 ([`7ffbd60`](https://github.com/Byron/gitoxide/commit/7ffbd602c08605026b0bb97ab85216907badaf09))
+ - 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))
+ - Bump transport version to 0.10 ([`f26a3d3`](https://github.com/Byron/gitoxide/commit/f26a3d3a2745f3eb69d76e0cfd718a90cf74f003))
+ - (cargo-release) version 0.8.0 ([`ad6d7f9`](https://github.com/Byron/gitoxide/commit/ad6d7f9c2b4f8879d466e758fc9b51ece6879e96))
+ - (cargo-release) version 0.6.0 ([`d704bca`](https://github.com/Byron/gitoxide/commit/d704bca7de0a6591f35345c842d6418b36ecd206))
+ - (cargo-release) version 0.7.0 ([`2ef3106`](https://github.com/Byron/gitoxide/commit/2ef3106eb84981e2dabd84f81362b4e44f938ea6))
+ - (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))
+ - [transport] A much better name for 'is_stateful()` ([`f15f1e8`](https://github.com/Byron/gitoxide/commit/f15f1e85fda76eef72c3754d625cf51e3c454eea))
+ - (cargo-release) version 0.3.0 ([`0e9c73a`](https://github.com/Byron/gitoxide/commit/0e9c73abd17e0dd21952275077ae53ad7e7aa1af))
+ - (cargo-release) version 0.16.0 ([`1231dbd`](https://github.com/Byron/gitoxide/commit/1231dbd16dacefb39adec8e067c312d313a82e3c))
+ - Clippy on tests and thanks clippy ([`a77a71c`](https://github.com/Byron/gitoxide/commit/a77a71cf02d328a2a964388928d6b2a235a0aa85))
+ - Thanks clippy ([`e1964e4`](https://github.com/Byron/gitoxide/commit/e1964e43979b3e32a5d4bfbe377a842d2c0b10ea))
+ - Bump async-trait from 0.1.50 to 0.1.51 ([`ce0b81e`](https://github.com/Byron/gitoxide/commit/ce0b81e8f5c652d389ff876844bc42bcfa687921))
+ - [transport] more convenient check for available capabilities ([`e9ed952`](https://github.com/Byron/gitoxide/commit/e9ed952d35fa9ffa142f941d75c385abec3997ef))
+ - Bump futures-io from 0.3.15 to 0.3.16 ([`3c23820`](https://github.com/Byron/gitoxide/commit/3c23820d3f0d3567f44215cdb0ad13ab675a201f))
+ - 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))
+ - [transport] remove Transport::close()… ([`4268a9b`](https://github.com/Byron/gitoxide/commit/4268a9bcf733413f7326be7af487a8fcdec1f71c))
+ - [transport] implement Transport for &mut T: Transport as well ([`372fb81`](https://github.com/Byron/gitoxide/commit/372fb8183aff19bd0f2d17ea74409b2ca3a08511))
+ - [transport] tests for extra parameters ([`fffd926`](https://github.com/Byron/gitoxide/commit/fffd926a3d5c6abfa732aa2305a4a05fdd06254d))
+ - [protocol] extra_parameters are forwarded from delegate to handshake ([`03e3db3`](https://github.com/Byron/gitoxide/commit/03e3db3809bd031d7d0c151ada2542214d7e32c0))
+ - [transport] allow setting a custom URL in git::Connection ([`f7437e0`](https://github.com/Byron/gitoxide/commit/f7437e041b2f3a8d51012972bd443d3c4b0a9252))
+ - [transport] async transports support extra params ([`a0d6756`](https://github.com/Byron/gitoxide/commit/a0d67569b5c947d8158177e308f0919df2f182a3))
+ - [transport] extra_headers for http ([`6026dcc`](https://github.com/Byron/gitoxide/commit/6026dcc07674ee9ea79503aab07491dd395e51a4))
+ - [transport] extra-parameters for the http protocol ([`d30bcf1`](https://github.com/Byron/gitoxide/commit/d30bcf18afda19d89b8cb020e193405bfd2d3787))
+ - [transport] git::Connection handles extra-parameters ([`961b6a4`](https://github.com/Byron/gitoxide/commit/961b6a40aaa003497abbd17fd53485c6cb2b2857))
+ - [transport] File implementation doesn't need to inherit git::Connection's… ([`951b1e2`](https://github.com/Byron/gitoxide/commit/951b1e26a1dca054aa5af6a565fde3c733b43ffd))
+ - [transport] unsupported protocol versions now abort the fetch operation ([`812aa3b`](https://github.com/Byron/gitoxide/commit/812aa3bc02a823cb9277847db905e76a50ee7413))
+ - [transport] flexible version of version support check doesn't actually work :D ([`2b220f0`](https://github.com/Byron/gitoxide/commit/2b220f0758cb7a96a66b256552f13a020cdee3fc))
+ - [transport] improve docs for `is_stateful()` ([`22f7e67`](https://github.com/Byron/gitoxide/commit/22f7e6719d2e3931f8cd4bb4e94c23c1f9f84189))
+ - Merge branch 'pubcap' ([`292f8ff`](https://github.com/Byron/gitoxide/commit/292f8ff2851dff846eda1b80943de718f08e65be))
+ - Add missing docs ([`a6cbbde`](https://github.com/Byron/gitoxide/commit/a6cbbdeecbfe459556579a2d991bd546452c04c3))
+ - Make capabilities parsing public ([`2f3725e`](https://github.com/Byron/gitoxide/commit/2f3725efcaa439db4e10ade1b9fbeb1258fd93c1))
+ - Thanks clippy ([`6200ed9`](https://github.com/Byron/gitoxide/commit/6200ed9ac5609c74de4254ab663c19cfe3591402))
+ - [async-client] unblock the async delegate in the cheapest possible way… ([`a3b5d75`](https://github.com/Byron/gitoxide/commit/a3b5d75d387dc5d6c44f695f63df8803613637a2))
+ - Revert "[async-client] Try to bring 'Send' back but…" ([`52eb953`](https://github.com/Byron/gitoxide/commit/52eb953fcc44cce19604b1df6a600237b8c81392))
+ - [async-client] Try to bring 'Send' back but… ([`3a06adb`](https://github.com/Byron/gitoxide/commit/3a06adb41f6b2946f78044e4ab1385e6441fc40f))
+ - Refactor ([`2a406d6`](https://github.com/Byron/gitoxide/commit/2a406d62c02db24c39980f9ae636b87e2d707faf))
+ - [async-client] frame for async connect ([`9ada080`](https://github.com/Byron/gitoxide/commit/9ada0805fc5896f8ef1a31dc821b789b7f0438a6))
+ - Prevent selecting mutually exclusive features ([`7f5da18`](https://github.com/Byron/gitoxide/commit/7f5da18c39b84af788ea1366ccca2c8b9d09f755))
+ - [git-transport] Fix http build ([`3469e99`](https://github.com/Byron/gitoxide/commit/3469e99afeaf354db4020d5a363b05a031894096))
+ - [git-protocol] fetch in sync and async… ([`4776039`](https://github.com/Byron/gitoxide/commit/47760399bffd030c848e0ef6df52a4765d8fb566))
+ - Bump maybe-async from 0.2.4 to 0.2.6 ([`d99a1a8`](https://github.com/Byron/gitoxide/commit/d99a1a815809d22c7384c6ecb1275e39fb911d91))
+ - Fix docs ([`bca7594`](https://github.com/Byron/gitoxide/commit/bca7594713d623e0f0a4b82b658c26ee9a041eaa))
+ - [git-protocol] fix build ([`4cce648`](https://github.com/Byron/gitoxide/commit/4cce6487d6d514541afee1a9aa92043f186136d3))
+ - [git-transport] refactor ([`d09153f`](https://github.com/Byron/gitoxide/commit/d09153f34135609224929f77175f3e0ac04ea12e))
+ - [git-transport] Properly implement Transport for Boxed types ([`47b10c9`](https://github.com/Byron/gitoxide/commit/47b10c9b4ccba5a0928819c92eda472bbfda0c50))
+ - [git-transport] refactor ([`3b0baee`](https://github.com/Byron/gitoxide/commit/3b0baee6b856b510ea839a1e294e9a99aafaa3ac))
+ - Refactor ([`2eefe17`](https://github.com/Byron/gitoxide/commit/2eefe1712131a69298be02e94df8b6ba844afcd9))
+ - Refactor ([`14c9093`](https://github.com/Byron/gitoxide/commit/14c909341d243ca3dcc42d343aeee65d28045b65))
+ - [git-protocol] async capabilities and arguments abstractions ([`aa3eacb`](https://github.com/Byron/gitoxide/commit/aa3eacbd53665d6b76bd9706d801d1189a970261))
+ - [git-transport] see how things can be moved to a different thread… ([`c271d32`](https://github.com/Byron/gitoxide/commit/c271d323086486a9d1dbe004b33fdb7d9eec45ed))
+ - [git-transport] partial transfer to thread doesn't work in test… ([`4a6dfd4`](https://github.com/Byron/gitoxide/commit/4a6dfd40e465226d0f9c7eb02cfb721b55bbff41))
+ - [git-transport] allow fetch processing to be offloading to another thread ([`a1302e0`](https://github.com/Byron/gitoxide/commit/a1302e0ff549e96362c441d8eecec56f1ef4ca43))
+ - Revert "[git-transport] async-executor (Local) hangs…" ([`ec8bcd0`](https://github.com/Byron/gitoxide/commit/ec8bcd0f36b46ff319ad6d6da9ffcb80dd5b0429))
+ - [git-transport] async-executor (Local) hangs… ([`68ac51b`](https://github.com/Byron/gitoxide/commit/68ac51b384cae11f5dca103160bf3d519305364e))
+ - Revert "[git-transport] attempt to mix 'blocking' but realize that now things need to be static." ([`e367753`](https://github.com/Byron/gitoxide/commit/e3677537d7858a113008849ac8ace136f5d5c4d2))
+ - [git-transport] attempt to mix 'blocking' but realize that now things need to be static. ([`3d296fa`](https://github.com/Byron/gitoxide/commit/3d296fae08ce0d4c2625008ab1fdcd7ede8dac54))
+ - [git-transport] V2 transport tests work on async ([`e04a1c9`](https://github.com/Byron/gitoxide/commit/e04a1c98a1ee0ba58fa326c9a68bd36230e229da))
+ - [git-transport] first V2 test ([`f9da975`](https://github.com/Byron/gitoxide/commit/f9da975ca777d2345e8c2842771b16a17af79cd3))
+ - [git-transport] adapt extension trait in blocking code to match async version ([`95eee30`](https://github.com/Byron/gitoxide/commit/95eee30191aed9421f69dcd6e1587c0b5a1f2dd2))
+ - [git-transport] extension trait working ([`28fbd28`](https://github.com/Byron/gitoxide/commit/28fbd284d108d5db3f13c8cede5772e065e5f8fb))
+ - [git-transport] a first step towards getting the extension trait to compile ([`b692979`](https://github.com/Byron/gitoxide/commit/b6929792c3d2ef9a73eb049eb88b06c8c763d899))
+ - [git-transport] no warnings when building without any choice of client ([`3dc568a`](https://github.com/Byron/gitoxide/commit/3dc568a3a29dfea6ff311cf965ecce7f7eddbf63))
+ - [git-transport] upgrade to futures-lite 1.12 with BufRead support ([`ee01c79`](https://github.com/Byron/gitoxide/commit/ee01c79887a892e001787bbefa93f75d9c4f1cfc))
+ - [git-transport] Show how to use blocking git-pack code in non-blocking transports ([`de2ba3c`](https://github.com/Byron/gitoxide/commit/de2ba3c4919d454894911c54fd4bb0e0a4665723))
+ - [git-transport] handshakeV1 tests run in async! ([`d1c0e35`](https://github.com/Byron/gitoxide/commit/d1c0e35817d183982a5b1eb7e545bfe83edb141e))
+ - [git-transport] And a chance to have V1 working in async ([`2bf93fc`](https://github.com/Byron/gitoxide/commit/2bf93fc72b3f9dcb63f8b24c77c95d518072431f))
+ - [git-transport] refactor ([`64bb8b3`](https://github.com/Byron/gitoxide/commit/64bb8b3937fcf7f14034ccfb6a72a24bf05f0320))
+ - [git-transport] improve error handling considerably… ([`7b7d314`](https://github.com/Byron/gitoxide/commit/7b7d314851b8db230228c28fb38a5a6541ec865c))
+ - [git-transport] Add remaninig git connection method… ([`73fcf38`](https://github.com/Byron/gitoxide/commit/73fcf38f9c334813572a6aeb7691758a524cac07))
+ - [git-transport] refactor ([`db83600`](https://github.com/Byron/gitoxide/commit/db83600179b3f27770b989f5f8ae1dd459749354))
+ - [git-transport] the first part of async transport for git connections ([`d94fbf8`](https://github.com/Byron/gitoxide/commit/d94fbf83d3e57c54dead3fb849e63b1d37343cb2))
+ - [git-transport] Split git connection into shared and blocking parts ([`0bfe693`](https://github.com/Byron/gitoxide/commit/0bfe69385932698b99871717d823fe645e4eabb8))
+ - [git-transport] refactor ([`2342e8a`](https://github.com/Byron/gitoxide/commit/2342e8a6a4ceca12603cb5e2c350edc2d4e71580))
+ - [git-transport] refactor ([`957403e`](https://github.com/Byron/gitoxide/commit/957403e11e0f7b3c97aa1996b2e936bbdb7ee12c))
+ - [git-transport] refactor ([`e580354`](https://github.com/Byron/gitoxide/commit/e58035452be10328c3e5e1991bafbab3f71d3353))
+ - [git-transport] re-enable `request()` method of main trait… ([`3adbade`](https://github.com/Byron/gitoxide/commit/3adbade31afa163a499fc2946f5af5ef3f367387))
+ - [git-transport] RequestWriter complete ([`a05fff3`](https://github.com/Byron/gitoxide/commit/a05fff3b2d987a5750e11945306bfa3731ed5ca3))
+ - [git-transport] refactor ([`03a3aed`](https://github.com/Byron/gitoxide/commit/03a3aedf17a91465279800d8028cc7435326534a))
+ - [git-transport] ARGH: PIN!!! ([`71379ac`](https://github.com/Byron/gitoxide/commit/71379ac25c44bc744ed0e93d2b126d4959bc4469))
+ - [git-transport] naive attempt to make Request async… ([`b819546`](https://github.com/Byron/gitoxide/commit/b819546b0096a4abfbe5ada25a1ac661a084cfc9))
+ - [git-transport] ExtendedBufRead for Async… ([`d4e56c8`](https://github.com/Byron/gitoxide/commit/d4e56c8efd586b571445e0085ce518c5efb8f5e6))
+ - [git-transport] First stab at ExtendedBufRead, but… ([`13f73d2`](https://github.com/Byron/gitoxide/commit/13f73d2f9b65d5ea829185af669532c3797cf90b))
+ - [git-transport] put request writer into general spot… ([`af07ebf`](https://github.com/Byron/gitoxide/commit/af07ebf44fbb3386cd5176441fd707cc820b71d0))
+ - [git-transport] refactor ([`5f98ac1`](https://github.com/Byron/gitoxide/commit/5f98ac140f9f3260d3d5a784d1aa1e1ac8c37114))
+ - [git-transport] fix docs ([`fbfc827`](https://github.com/Byron/gitoxide/commit/fbfc8271431f7c19adbed5e095d7c2ee10dda5e5))
+ - [git-transport] refactor ([`011ece0`](https://github.com/Byron/gitoxide/commit/011ece04827d75aa6d93e9fcae449aaba4167f80))
+ - [git-transport] the first async trait ([`2abac2a`](https://github.com/Byron/gitoxide/commit/2abac2a2df8033c6d2578e5afb88bb34aab86988))
+ - [git-transport] refactor ([`73df129`](https://github.com/Byron/gitoxide/commit/73df12987a9255efc4724e1761f335a072d3bcaf))
+ - [git-transport] the first async-only type ([`88109a5`](https://github.com/Byron/gitoxide/commit/88109a54ad594df6d18cf6b66a9c89a76fc0cdf5))
+ - [git-transport] all non-IO types are now shared ([`209c780`](https://github.com/Byron/gitoxide/commit/209c780efff32d63ce7edc8b1f92fac0cd1a396d))
+ - [git-transport] feature toggle for async-client; prepare for test ([`95e6801`](https://github.com/Byron/gitoxide/commit/95e6801b121a0744908552090b855cb3dbe99e64))
+ - [git-transport] refactor ([`592d9ac`](https://github.com/Byron/gitoxide/commit/592d9ac03b0d26848435a43480c526a4a0e0efb8))
+ - [git-transport] remove maybe_async from dependencies, add async-client feature ([`e57aad3`](https://github.com/Byron/gitoxide/commit/e57aad3a19ec89fd0aa4d8670430434f0dc4c826))
+ - (cargo-release) version 0.15.0 ([`d69d9fb`](https://github.com/Byron/gitoxide/commit/d69d9fb0931f8257cef96ef14a89da9340ad9738))
+ - [git-packetline] Use io::(Result|Error) everywhere ([`374f129`](https://github.com/Byron/gitoxide/commit/374f129e0d1473db9a2107c408f655da032efe89))
+ - [git-packetline] refactor ([`f038ca1`](https://github.com/Byron/gitoxide/commit/f038ca1e1c6d99bfcedb0387abc4151b188750c6))
+ - [git-packetline] document feature toggle ([`8b8a1aa`](https://github.com/Byron/gitoxide/commit/8b8a1aafb04b2e305cf2674c15530b430dad4969))
+ - [git-packetline] refactor ([`1328c5b`](https://github.com/Byron/gitoxide/commit/1328c5b4001f380936beff73e1f822f14e41e98b))
+ - (cargo-release) version 0.6.0 ([`ec5a54e`](https://github.com/Byron/gitoxide/commit/ec5a54e9f3543afddc9f972f16135edc6ef6ff5b))
+ - [git-packetline] refactor ([`e5769d1`](https://github.com/Byron/gitoxide/commit/e5769d1e7668ae54c667d2593c0c22e7723710c0))
+ - [git-packetline] refactor ([`fef3c9f`](https://github.com/Byron/gitoxide/commit/fef3c9f0aed3f6a509a71e8ff20050c6ea660f56))
+ - (cargo-release) version 0.9.0 ([`18f6d01`](https://github.com/Byron/gitoxide/commit/18f6d011043203523f1d0dacf657704ed3f9cf89))
+ - [git-transport] simplify parsing capabilities from lines ([`401af09`](https://github.com/Byron/gitoxide/commit/401af0974742f10c8b9b3c9752e9d30205e96c16))
+ - Refactor ([`8ce28e7`](https://github.com/Byron/gitoxide/commit/8ce28e74545ded2909417df9091da866fb343710))
+ - [git-transport] test capabilities in blocking and async mode ([`66eb2a5`](https://github.com/Byron/gitoxide/commit/66eb2a5c803d3365c8d9522f24843a8b73dff76d))
+ - Refactor ([`558b208`](https://github.com/Byron/gitoxide/commit/558b208f5055ab0562d3704e4fb62693eaab94fe))
+ - [git-transport] first round of getting capabilities into 'dual' mode… ([`3af353b`](https://github.com/Byron/gitoxide/commit/3af353b8b3ed0ee608cbc96d1cd45a3165907a12))
+ - [git-transport] remove default features to force being explicit everywhere ([`d1b39f8`](https://github.com/Byron/gitoxide/commit/d1b39f8093c032a172237a584c9208479611a866))
+ - [git-transport] A first async test, right now there is nothing to test though ([`9741ae1`](https://github.com/Byron/gitoxide/commit/9741ae1ee0fcf65c144d87cd17d8fe547b288b12))
+ - Tests follow crate structure closely (again) ([`8d6e46a`](https://github.com/Byron/gitoxide/commit/8d6e46a84c41d7f04f2dbbb1a4602159b1a96c8b))
+ - Make the blocking client the default… ([`9d62ca3`](https://github.com/Byron/gitoxide/commit/9d62ca338927139708246ce0934f1cb317f14784))
+ - Revert "Remove maybe-async for now" ([`ebd5701`](https://github.com/Byron/gitoxide/commit/ebd57017f80d0c12f6fe9a8d236843323c638311))
+ - Refactor ([`84d1509`](https://github.com/Byron/gitoxide/commit/84d150952d9cd72a05d83419d4fc013c75d7b2dc))
+ - Refactor ([`1412282`](https://github.com/Byron/gitoxide/commit/141228219d33e8056489514f91221d803888edd8))
+ - Refactor ([`f16d057`](https://github.com/Byron/gitoxide/commit/f16d057054ad6fabc664bbcb00f75e5974f05db9))
+ - Refactor ([`976da51`](https://github.com/Byron/gitoxide/commit/976da51efc5720300dfd3093e377284d1c4ccf3c))
+ - Refactor ([`7ac6a05`](https://github.com/Byron/gitoxide/commit/7ac6a05442b0d7e97c886f8f57b8695a14761027))
+ - Refactor ([`cd02749`](https://github.com/Byron/gitoxide/commit/cd027498944abde8339c421b5527abb9c3495de3))
+ - Remove maybe-async for now ([`97e96f4`](https://github.com/Byron/gitoxide/commit/97e96f49ce9e4ac325faebe112cec9c11cdc715c))
+ - Refactor ([`6e6f4ac`](https://github.com/Byron/gitoxide/commit/6e6f4acf4b3c704989928347f10f1725e6a866bd))
+ - Refactor git-transport test in preparation for async testing ([`42d5bf7`](https://github.com/Byron/gitoxide/commit/42d5bf7f5e8f4d88d3f849febec0c6d2678e0d06))
+ - (cargo-release) version 0.8.0 ([`411a05e`](https://github.com/Byron/gitoxide/commit/411a05ead1546c76fe51f359fbcb961a1140535e))
+ - (cargo-release) version 0.5.0 ([`8c4cc3f`](https://github.com/Byron/gitoxide/commit/8c4cc3fb5922d1a761463bbbad65e59f91cce4cb))
+ - [async-transport] Cargo.toml and traits to be more 'realistic' ([`9a617a5`](https://github.com/Byron/gitoxide/commit/9a617a5e68f032dbaba9a902b558666992683701))
+ - [async-transport] The very first step ([`b9e5559`](https://github.com/Byron/gitoxide/commit/b9e5559afa776dc1a7eecc90cc219da5ff911d79))
+ - (cargo-release) version 0.14.0 ([`a760f8c`](https://github.com/Byron/gitoxide/commit/a760f8c013e13ba82daa1acf1a4a57e0818a008d))
+ - (cargo-release) version 0.13.0 ([`ac2eddb`](https://github.com/Byron/gitoxide/commit/ac2eddb06eb3d8a9a3dcdcd796eb54a7e45ab935))
+ - (cargo-release) version 0.7.0 ([`334b7e1`](https://github.com/Byron/gitoxide/commit/334b7e1b838b5201f2484be42dee3c4d2fd789d7))
+ - (cargo-release) version 0.12.0 ([`3b71e7e`](https://github.com/Byron/gitoxide/commit/3b71e7e8416e550b47e5aed2259c1181497ac9e8))
+ - (cargo-release) version 0.6.0 ([`50fb6f2`](https://github.com/Byron/gitoxide/commit/50fb6f25e9afa900ac1c3cfb88d7ca0d5a9a95f7))
+ - (cargo-release) version 0.3.0 ([`d5c6643`](https://github.com/Byron/gitoxide/commit/d5c6643a41d295eaf7aabb84eab435e42a11dd42))
+ - Thanks clippy ([`749ceba`](https://github.com/Byron/gitoxide/commit/749ceba246fb8a4cb8d48fa86184619fef500108))
+ - Merge pull request #50 from Byron/edward-shen/odb-zlib-ng ([`acb90d7`](https://github.com/Byron/gitoxide/commit/acb90d755fb02c37f8a5a431778abcbe143fb5e5))
+ - [gix] Add optional zlib feature ([`f1f9665`](https://github.com/Byron/gitoxide/commit/f1f96658a6cd6165ba9c9d7acb809fcaf2c46f9c))
+ - (cargo-release) version 0.11.0 ([`1aa1f5e`](https://github.com/Byron/gitoxide/commit/1aa1f5e84a07427d5d7f3231735fe9c1923f506f))
+ - (cargo-release) version 0.2.0 ([`0c39373`](https://github.com/Byron/gitoxide/commit/0c39373de5aba0acc4aaa330bf51b6abd4f50474))
+ - Support for radicle urls ([`2c5b955`](https://github.com/Byron/gitoxide/commit/2c5b955b07073c5ef0e7bbe3ab20f0047770440b))
+ - (cargo-release) version 0.5.1 ([`0cf1d06`](https://github.com/Byron/gitoxide/commit/0cf1d06f5e289b541a842af03b59229fec833ca7))
+ - Silence so far unknown clippy lints ([`b5f2a4b`](https://github.com/Byron/gitoxide/commit/b5f2a4b079665daa8b9e0228acc59d1eddd603b2))
+ - Thanks clippy ([`343ab9a`](https://github.com/Byron/gitoxide/commit/343ab9adb62da1dde495fc209c179137bbe59a10))
+ - Complete git-transport docs ([`fa2dc9d`](https://github.com/Byron/gitoxide/commit/fa2dc9d65100f0f3f97358746a37dc722bae12c3))
+ - Documentation for capabilities in git-transport ([`5ec79fa`](https://github.com/Byron/gitoxide/commit/5ec79faaa2568cee9333b4bb0c96e8f0ee5a2433))
+ - More docs for git-transport ([`3a867e9`](https://github.com/Byron/gitoxide/commit/3a867e945edad05dd65b75111628b99fa955c03f))
+ - More git-transport docs ([`6cd69b9`](https://github.com/Byron/gitoxide/commit/6cd69b9a6b79ba8f9297cf2bb6e36dd8f63845a2))
+ - (cargo-release) version 0.5.0 ([`28df5e9`](https://github.com/Byron/gitoxide/commit/28df5e9131aec3efb2b68db204662b92b232b33c))
+ - Use git-hash in git-features ([`5b307e0`](https://github.com/Byron/gitoxide/commit/5b307e076f6f5975592c8b177c122c91c1d809c6))
+ - (cargo-release) version 0.4.0 ([`32aefc0`](https://github.com/Byron/gitoxide/commit/32aefc051c7ad9d1a160f77db070df7fa4843dbc))
+ - (cargo-release) version 0.4.0 ([`72eaece`](https://github.com/Byron/gitoxide/commit/72eaeceed135e4cc5c943685f4c902d03597c4d2))
+ - (cargo-release) version 0.9.0 ([`a89fdb9`](https://github.com/Byron/gitoxide/commit/a89fdb98f64bb0ca070fa79a1f58f1232bb14090))
+ - (cargo-release) version 0.3.0 ([`d19ee35`](https://github.com/Byron/gitoxide/commit/d19ee35cc6683c63e0eabd717e4758075faeaa71))
+ - (cargo-release) version 0.3.0 ([`eade7d1`](https://github.com/Byron/gitoxide/commit/eade7d101e071153055b07d9c6ae3c1452493a21))
+ - Thanks clippy ([`ba9b3c2`](https://github.com/Byron/gitoxide/commit/ba9b3c2345887353e02fc081be80733f1c5e22d9))
+ - Uograde everything else ([`0cd79d0`](https://github.com/Byron/gitoxide/commit/0cd79d00bce3f042b5cc849cf48739e29f95fcb0))
+ - (cargo-release) version 0.8.0 ([`47c00c2`](https://github.com/Byron/gitoxide/commit/47c00c2228cf25c79e1fa3eb4229c7ab24de91e5))
+ - Refactor ([`b3a8bb5`](https://github.com/Byron/gitoxide/commit/b3a8bb5f7f0c6e80259922546928c2739c24f7b5))
+ - Refactor ([`f9e8d29`](https://github.com/Byron/gitoxide/commit/f9e8d2932c02c22bf57acd39fb0a9e6d521070bd))
+ - Cargo clippy Rust 1.48 ([`475a68c`](https://github.com/Byron/gitoxide/commit/475a68ce33b895de911939c51afa159df534f7b8))
+ - (cargo-release) version 0.7.0 ([`7fa7bae`](https://github.com/Byron/gitoxide/commit/7fa7baeb3e7d008a25e4d714eff908e2516c828b))
+ - Remove dash in all repository links ([`98c1360`](https://github.com/Byron/gitoxide/commit/98c1360ba4d2fb3443602b7da8775906224feb1d))
+ - Merge from main. ([`b59bd5e`](https://github.com/Byron/gitoxide/commit/b59bd5e0b0895c7d1d585816cec8be4dea78c278))
+ - Refactor ([`ba1d883`](https://github.com/Byron/gitoxide/commit/ba1d88364424eb60a0874a5726b62740dc348592))
+ - (cargo-release) version 0.2.1 ([`ebf3419`](https://github.com/Byron/gitoxide/commit/ebf341936b22e899de88293ef377b438353d1821))
+ - (cargo-release) version 0.6.0 ([`9ef184e`](https://github.com/Byron/gitoxide/commit/9ef184e35712f938fb4f9f6da7390a8777a9284e))
+ - (cargo-release) version 0.2.0 ([`779e9d0`](https://github.com/Byron/gitoxide/commit/779e9d0ad67c20fa9cec14359e87774ca2d74ee4))
+ - (cargo-release) version 0.2.0 ([`da830de`](https://github.com/Byron/gitoxide/commit/da830defc9cfa81ce159f6d908da828227760845))
+ - (cargo-release) version 0.5.0 ([`82b7313`](https://github.com/Byron/gitoxide/commit/82b73131b79ec3c42a712dad1c0766a72209d737))
+ - Thanks clippy ([`e5d80b1`](https://github.com/Byron/gitoxide/commit/e5d80b19b83dc03d49606b7ccba20ff0c39bc5d9))
+ - [clone] make cloning the linux kernel work ([`e780526`](https://github.com/Byron/gitoxide/commit/e78052649c734f16f4d154edcbf54f4cc4484f5e))
+ - [clone] Assure we don't hang due to unprocessed headers when peeking lines! ([`d9ced27`](https://github.com/Byron/gitoxide/commit/d9ced2711dba702d73b28f0e1b9399cd7eab5183))
+ - [clone] none the wiser - it really looks like everything is alright… ([`3b8d613`](https://github.com/Byron/gitoxide/commit/3b8d613c6de349defce9af06d56f73ac2c0d0d25))
+ - [clone] reassure ourselves that ERR lines are handled, always ([`925370b`](https://github.com/Byron/gitoxide/commit/925370b3f1d701d3376bdc80a4876e407b54c400))
+ - [clone] Response parsing up to (optional) pack ([`24064c7`](https://github.com/Byron/gitoxide/commit/24064c77f2969380fb92ea66109df86e84060324))
+ - [clone] properly handle V2 response parsing ([`0d7d768`](https://github.com/Byron/gitoxide/commit/0d7d768278234824e03c5e74dacaafca3ee65713))
+ - [ref-ls] A way to abort on multiple delimiters; first tests work ([`8d44912`](https://github.com/Byron/gitoxide/commit/8d44912e7215b85c6931b7b829bd73ac38584424))
+ - Refactor ([`feec5be`](https://github.com/Byron/gitoxide/commit/feec5be335a99a4c47ba98f93803863044575838))
+ - [ref-ls] Allow multiple delimiters at the same time ([`cfae63a`](https://github.com/Byron/gitoxide/commit/cfae63a5f7d2d99560dd857f7220980d70c4c4d8))
+ - [ref-ls] It would be practical to simply have access to the line provider… ([`5fba787`](https://github.com/Byron/gitoxide/commit/5fba78796d3bcc16f812dc3202d521ee057e86f9))
+ - Thanks clippy ([`27f30df`](https://github.com/Byron/gitoxide/commit/27f30df9a8046fe4e872837e36dd497096660282))
+ - [ref-ls] don't leak the PacketLine error type in Transport interface ([`58ddd29`](https://github.com/Byron/gitoxide/commit/58ddd2955a407efb6a46249810b916c6035eb5ff))
+ - [ref-ls] support for line peeking in packet line readers ([`0c0c575`](https://github.com/Byron/gitoxide/commit/0c0c57522972f2a49ed5261474114da062e6ab15))
+ - [ref-ls] don't enforce V1 for local interactions ([`7b33336`](https://github.com/Byron/gitoxide/commit/7b333369de1221f9bfbbe03a3a13e9a09bc1c907))
+ - [ref-ls] don't do anything on drop ([`9f18d9b`](https://github.com/Byron/gitoxide/commit/9f18d9b9062d61d6da6e2bb7564fe5edbb1528c4))
+ - [ref-ls] Transport layer knows whether it's stateful or not ([`22c3640`](https://github.com/Byron/gitoxide/commit/22c3640b70bb6925d72794eeaeda48b0687f2047))
+ - [ref-ls] Always send a flush before closing the connection ([`918f19f`](https://github.com/Byron/gitoxide/commit/918f19f0c2dc202ed2014e30b7247e63a0f6a51e))
+ - [ref-ls] git protocol now supports user expansion… ([`d88e9da`](https://github.com/Byron/gitoxide/commit/d88e9da15068aede3a587cbc857702fcfbdc6c6e))
+ - Refactor ([`e07fbd6`](https://github.com/Byron/gitoxide/commit/e07fbd63db297cd9f70f8b86b1f1f56b15e270a8))
+ - Refactor ([`7b5ce69`](https://github.com/Byron/gitoxide/commit/7b5ce695a05630c20ab461c63ff1f9b5fc662958))
+ - [ref-ls] allow ssh to work with tildes in paths ([`301ae81`](https://github.com/Byron/gitoxide/commit/301ae81d1062fb002b080e8cdbb0bec134dd4de6))
+ - [ref-ls] first stab at leaving path resolution to upload pack ([`51dad09`](https://github.com/Byron/gitoxide/commit/51dad09221c118884db7e52d1337eb2ab476e744))
+ - [ref-ls] verify also ssh works ([`1ef39ae`](https://github.com/Byron/gitoxide/commit/1ef39aed71a684157363e27d0ae092a616782d41))
+ - [ref-ls] tune request to actually work in all cases, particularly for github ([`6bab2f3`](https://github.com/Byron/gitoxide/commit/6bab2f343347dca45288a9f17f1b05fc62611080))
+ - [ref-ls] Make credentials helper truly work ([`7f3c3a7`](https://github.com/Byron/gitoxide/commit/7f3c3a71db7eeba1d37481ba1b522d5ded654237))
+ - [ref-ls] Finally fix http content encoding (and fixtures to go with it) ([`49b7ad9`](https://github.com/Byron/gitoxide/commit/49b7ad97075474ced5232345c7afac9d657c72b4))
+ - [ref-ls] This actually makes things work in real-life ([`24ebc59`](https://github.com/Byron/gitoxide/commit/24ebc59d669dd22c68efa76eb3bcd66b6b59a3dd))
+ - [ref-ls] provide blanket impl at least to be less specific ([`0223e7f`](https://github.com/Byron/gitoxide/commit/0223e7f4bf3eb5b3d3f3f430d82ce3386a6a566e))
+ - [ref-ls] Make things compile ([`b6506a4`](https://github.com/Byron/gitoxide/commit/b6506a46ef59d8e25b245fa8caac5b4de4fdaa3d))
+ - Refactor ([`b38290e`](https://github.com/Byron/gitoxide/commit/b38290e4a8fcabd758f26a15407710ab2abcdc07))
+ - Refactor ([`202383a`](https://github.com/Byron/gitoxide/commit/202383add69e7667fb2043d55e17f8064bc658c9))
+ - Thanks clippy ([`b060f42`](https://github.com/Byron/gitoxide/commit/b060f42d097e52b5891e3b774ebd8ea8b076d011))
+ - [clone] support automatic downgrade to protocol version 1 ([`4cf3643`](https://github.com/Byron/gitoxide/commit/4cf36436f11eb95d420c1147a1ec8adb618ea5fb))
+ - [clone] transport provides desired protocol version ([`c39b645`](https://github.com/Byron/gitoxide/commit/c39b64598a4119f0cf3c8aaf30e32996632fb51c))
+ - [clone] update README, improve delegate docs ([`dc7908f`](https://github.com/Byron/gitoxide/commit/dc7908f1546239ade71f4147a389a001769311f5))
+ - [clone] features for V1 fetch ([`5b24a55`](https://github.com/Byron/gitoxide/commit/5b24a559dfb03c99ee360e9997650c443fd30077))
+ - [clone] Support explicitly closing (v2) connections ([`41e4cb2`](https://github.com/Byron/gitoxide/commit/41e4cb22de9e06bfc5aa93246f931f483335fa69))
+ - Refactor ([`dda62fc`](https://github.com/Byron/gitoxide/commit/dda62fc1a170793768ef1791db85a0c3cca0fad1))
+ - [clone] Prevent accidental leakage by transforming back to the 'right' type ([`2d469c6`](https://github.com/Byron/gitoxide/commit/2d469c66ec47be2e1bc3e0b1f3d17dfea5050970))
+ - Refactor ([`88ecda1`](https://github.com/Byron/gitoxide/commit/88ecda11dc1d97a7460a449350945dcac2f13752))
+ - [clone] support for git-credentials helper ([`a6546da`](https://github.com/Byron/gitoxide/commit/a6546dab8d6d0dc4453052b77278cf5bb96aaade))
+ - [clone] make URL available in transport layer ([`6778447`](https://github.com/Byron/gitoxide/commit/67784478b96f8afd142e52982e2161a1f05d2ec9))
+ - [clone] http basic auth support for all kinds of calls ([`572fb54`](https://github.com/Byron/gitoxide/commit/572fb54b1445d25d55713ca3d68e19bede2b3cff))
+ - [clone] first sketch of basic authentication support for http ([`c5b2d04`](https://github.com/Byron/gitoxide/commit/c5b2d046f58a8cd3b66097c48ea9399ac34246d7))
+ - [clone] sketch for identity handling ([`b23f470`](https://github.com/Byron/gitoxide/commit/b23f47029fba50c7bba23a6ebe135e129ee9392a))
+ - Refactor ([`22cb37d`](https://github.com/Byron/gitoxide/commit/22cb37d26f18653b81f52e23f58b5797c763f883))
+ - [clone] Add (hardcoded) timeout when connecting via TCP ([`02c195b`](https://github.com/Byron/gitoxide/commit/02c195ba312b174ada5733321c08a8294f360cdd))
+ - Thanks clippy ([`712527f`](https://github.com/Byron/gitoxide/commit/712527f2adfad18c2527ee7bf9bb8841897db4e0))
+ - [clone] Finish implementing ssh access, which might even work ([`8b843f2`](https://github.com/Byron/gitoxide/commit/8b843f2f08c3b070db427a3a8f2c08f4d778914c))
+ - [clone] Add support for SSH prefixes in otherwise local service invocations ([`a1db217`](https://github.com/Byron/gitoxide/commit/a1db2172dc691765dec907a226e0790c36358c1f))
+ - [clone] once again, for SSH we need to delay calling the actual service ([`2c70275`](https://github.com/Byron/gitoxide/commit/2c70275e45b487a966d1772cf1e7a90e96cbbaad))
+ - [clone] Support for the probably very unnkown VIRTUAL_HOST env var ([`36fe20c`](https://github.com/Byron/gitoxide/commit/36fe20cb3a82fe6fa78cc18f7d71d25b9022397c))
+ - [clone] Allow connecting via the protocol ([`eb7be2b`](https://github.com/Byron/gitoxide/commit/eb7be2bd5f75ac3a04fb1b6afdc9377478f7818e))
+ - [clone] be sure to wait on the spawned child on drop to prevent resource depletion ([`768d7f2`](https://github.com/Byron/gitoxide/commit/768d7f2b704f569e117a63a690c3b3769a2b1442))
+ - Thanks clippy ([`2528c82`](https://github.com/Byron/gitoxide/commit/2528c82f4708a0258985d3ebfde6cba10a72c9c6))
+ - [clone] implement file based protocol using git-<service> processes ([`be254a9`](https://github.com/Byron/gitoxide/commit/be254a9316c08e17702eeb4c65b4dde8e6bb2e6e))
+ - [clone] add 'Process' mode for git connection ([`e38c7bf`](https://github.com/Byron/gitoxide/commit/e38c7bf1a2a9409762e891b7bb50f509d9b0d03d))
+ - Refactor ([`2ecb975`](https://github.com/Byron/gitoxide/commit/2ecb9759f6a916c0a887800df625480c123aa5f6))
+ - [clone] first steps towards launching git-upload-pack while… ([`41f05f1`](https://github.com/Byron/gitoxide/commit/41f05f13a1fac078b694e6f4a9c8f52eeaff4191))
+ - [clone] Http fetch test for V2 ([`81618ae`](https://github.com/Byron/gitoxide/commit/81618ae8f4e60fbfbb424de6e42bf796dabb47f8))
+ - [clone] http test for ls-refs V2 ([`3ef1e47`](https://github.com/Byron/gitoxide/commit/3ef1e47d7e3781bdc52daac0d266dcbaa3dfb07a))
+ - [clone] finish test for git-based V2 command invocation ([`9384f32`](https://github.com/Byron/gitoxide/commit/9384f327e6c9bf5d6fc6f17d5d0ed573213fc5d8))
+ - [clone] support for V2 arguments ([`8d56e79`](https://github.com/Byron/gitoxide/commit/8d56e7961c7de15197d1127617194fde028cc2aa))
+ - Refactor ([`f46c89d`](https://github.com/Byron/gitoxide/commit/f46c89d087e387702d2f887946ff0da1fdb19117))
+ - Refactor ([`9ed859a`](https://github.com/Byron/gitoxide/commit/9ed859a5ebd8f4a1654107c9909f99739c73435d))
+ - [clone] Using a normal writer, we can't produce delimiter packets ([`1877b5f`](https://github.com/Byron/gitoxide/commit/1877b5f09d65fac6963b75afdf22afa938c7aac8))
+ - [clone] first sketch of extension trait to invoke V2 commands ([`90eed9d`](https://github.com/Byron/gitoxide/commit/90eed9d5a8a672fe6c899c07b98b51e1e783b656))
+ - [clone] Finally, HTTP requests are properly handled, it all works out! ([`a6121d9`](https://github.com/Byron/gitoxide/commit/a6121d93eec50406cbd0b1b8a8f1fbbbabec0f53))
+ - [clone] Helper now integrates with Http trait, neat ([`b462bc7`](https://github.com/Byron/gitoxide/commit/b462bc7abbb8468063d85d08d656b820fdac903e))
+ - [clone] first sketch of 'HeaderAndBody' helper ([`226f096`](https://github.com/Byron/gitoxide/commit/226f0967b557f1186ce5a1ca6f6e80e6926d3210))
+ - Refactor ([`f2ff90d`](https://github.com/Byron/gitoxide/commit/f2ff90d65edd91c4f6dc6baaf1242df31ef0ef2e))
+ - [clone] a way to change progress handling on the fly ([`c1bcc0a`](https://github.com/Byron/gitoxide/commit/c1bcc0adf04a32e9332fae047fba066d4cff6538))
+ - [clone] first steps towards more flexible sideband switching ([`3d959e6`](https://github.com/Byron/gitoxide/commit/3d959e68f1b4906ac143e26eb14d65bdf03ef62a))
+ - [clone] Issue: shoehorning header handling into the body reader ([`4c304f1`](https://github.com/Byron/gitoxide/commit/4c304f1520fdc06c03aceb389049154cc53edea9))
+ - Thanks clippy ([`bdcaf36`](https://github.com/Byron/gitoxide/commit/bdcaf3680a13fde84ff7f9cbe3f49b09c8e55d8f))
+ - [clone] Now we get to the point where uploads start, but… ([`8bd6182`](https://github.com/Byron/gitoxide/commit/8bd618262c963b23f84ad5214b25efe474abb851))
+ - [clone] first steps towards testing posting via http… ([`b6a7e75`](https://github.com/Byron/gitoxide/commit/b6a7e752053254a3547c9afcb6254201fd9f69a8))
+ - Refactor ([`a810f9f`](https://github.com/Byron/gitoxide/commit/a810f9f354a5e532b819d542ed2a2fdf7a42eb17))
+ - Refactor ([`5c2bd5f`](https://github.com/Byron/gitoxide/commit/5c2bd5f244bacc776be6f5f45dfd79f03b3a3093))
+ - [clone] make on-drop messages do the right thing ([`5a39d70`](https://github.com/Byron/gitoxide/commit/5a39d70c00dbe04598137f479dca54c1ec2dd98e))
+ - [clone] first test for request - ideally we manage to add a lifetime to the closure box… ([`db1a5b8`](https://github.com/Byron/gitoxide/commit/db1a5b83338b12a14c3cc53886f5362feef1370f))
+ - Thanks clippy ([`913e55d`](https://github.com/Byron/gitoxide/commit/913e55de74d3cfecba78345945f1b965b47d407d))
+ - Refactor ([`de22323`](https://github.com/Byron/gitoxide/commit/de22323c075de5bcb2957418410876101a05c9da))
+ - Refactor ([`bad8361`](https://github.com/Byron/gitoxide/commit/bad836163c101f74e1eea862698edcbf730a05d5))
+ - Refactor ([`466557c`](https://github.com/Byron/gitoxide/commit/466557c6c0e4c7b5afe94bee4a4ab07a714089b2))
+ - [clone] on-demand line writer, it's cheap ([`8ddd0fa`](https://github.com/Byron/gitoxide/commit/8ddd0fa9f74c9ac44e305d7fb0e53fb816bea0b6))
+ - [clone] it shows that the packetline writer is better to be owned ([`f2c6e9f`](https://github.com/Byron/gitoxide/commit/f2c6e9fa763d10f7c72ad4ff29f273f8a6c6872f))
+ - Refactor ([`aceaaed`](https://github.com/Byron/gitoxide/commit/aceaaed45be5d523c9b4c1f98444b84619cbc13f))
+ - Refactor ([`2cdda7a`](https://github.com/Byron/gitoxide/commit/2cdda7af8ae884b5efde8861f13d85b07d643b94))
+ - Refactor ([`521516f`](https://github.com/Byron/gitoxide/commit/521516f8c5713070ca393bd3b54994cb162d7523))
+ - Refactor ([`3738897`](https://github.com/Byron/gitoxide/commit/3738897a0347694458717e0abbb052ed79c3546d))
+ - Refactor ([`2e68315`](https://github.com/Byron/gitoxide/commit/2e68315a7a1e47d2659eb3c047ba6015475bd9bf))
+ - [clone] first sketch of http request ([`8b4befb`](https://github.com/Byron/gitoxide/commit/8b4befb9ef589d02f9232d72229731b646614fcf))
+ - Refactor ([`23af7e1`](https://github.com/Byron/gitoxide/commit/23af7e1e7c19d6e0db43540016ef9d851d0a048a))
+ - [clone] support writing multiple messages on drop for the 'clone' case ([`9266442`](https://github.com/Byron/gitoxide/commit/92664425cb70d3d646dab97a714efc7f7b99b96d))
+ - Thanks clippy ([`2ed10de`](https://github.com/Byron/gitoxide/commit/2ed10de5f6213ca5ed68f072b9984bff32a5d67c))
+ - [clone] Sketch 'request()' implementation for git protocol ([`fd0e0e9`](https://github.com/Byron/gitoxide/commit/fd0e0e9e49f5481c14e17a462f9e507663fd5e6a))
+ - [clone] Allow progress callback to know if it's an error line ([`0c41844`](https://github.com/Byron/gitoxide/commit/0c41844dbad5182ad3ea8d15dcfd0af92263936c))
+ - [clone] sketch for generic request/response pattern suitable for clone/fetch ([`e0fd5a6`](https://github.com/Byron/gitoxide/commit/e0fd5a60960e0768a68878f38c034be9c44c6039))
+ - Thanks clippy (what would I do without you <3) ([`631af04`](https://github.com/Byron/gitoxide/commit/631af04c87f0b6b22c3ac1ef0d300a02bbdcd217))
+ - [clone] Capabilities now can have multiple values (per command) for V2 ([`44dcea6`](https://github.com/Byron/gitoxide/commit/44dcea6ed33549e97cfbdd006f9f8fb3ce2e8597))
+ - [clone] First step towards http V2 handshake shows capabilities are… ([`f58a785`](https://github.com/Byron/gitoxide/commit/f58a78595658e30cac216c0ddade891cda745eae))
+ - [clone] remaining handshake V2 assertions ([`1a58955`](https://github.com/Byron/gitoxide/commit/1a58955f70a945e94d9846424c35c341a8661549))
+ - [clone] first sketch of git handshake, V2 ([`bf1f05b`](https://github.com/Byron/gitoxide/commit/bf1f05bbb229f98cfa636c2b974b687042168d20))
+ - [clone] git protocol sends in packet line format, which is now enforced ([`4ce5916`](https://github.com/Byron/gitoxide/commit/4ce591646c2c6958bc3cf67a7dc6f792d507c30b))
+ - Refactor ([`44b06a7`](https://github.com/Byron/gitoxide/commit/44b06a7d4b11494271e59ef3069f0fe326c9eadf))
+ - Thanks clippy ([`ee5abfc`](https://github.com/Byron/gitoxide/commit/ee5abfcf85889a56495844e4403bc40ebaa01a29))
+ - [clone] Configure http timeouts, just so that it is done ([`070855a`](https://github.com/Byron/gitoxide/commit/070855a5bda314c5843ea9091351e8f8540d7d05))
+ - Refactor ([`8b1dc48`](https://github.com/Byron/gitoxide/commit/8b1dc4846c54be78b2df0f3d02c4efa53c7f79a6))
+ - [clone] Allow differentiating HTTP permission errors ([`4c9c413`](https://github.com/Byron/gitoxide/commit/4c9c413c1cdd24dea59274931b335be3daf3653d))
+ - [clone] abort early on HTTP status errors ([`e829c0a`](https://github.com/Byron/gitoxide/commit/e829c0ab9923ff38c36196cbe196d158cb3a1ea7))
+ - Refactor ([`791c05e`](https://github.com/Byron/gitoxide/commit/791c05e08d9c6148fa0f89894c9b2abdadf3f503))
+ - [clone] more http test validations ([`e697b8c`](https://github.com/Byron/gitoxide/commit/e697b8cbc236783938fca70c0a4b46ccdf10c084))
+ - Revert "[clone] FAIL: try to communicate error codes after request" ([`350de7c`](https://github.com/Byron/gitoxide/commit/350de7cd517f8b94e263312f5dda1515ae6297a9))
+ - [clone] FAIL: try to communicate error codes after request ([`2501ddd`](https://github.com/Byron/gitoxide/commit/2501ddd9f377cc869817fb32be213d943a9666a0))
+ - [clone] Check for 'smart' protcols ([`2960645`](https://github.com/Byron/gitoxide/commit/2960645ffb160745cac82b2e7267dcff10286420))
+ - [clone] validate expected http service announcement ([`a224a2c`](https://github.com/Byron/gitoxide/commit/a224a2c8190b1d45a61eef4cdef620e7f3bea659))
+ - [clone] Keep line reader around in http transport ([`feb2596`](https://github.com/Byron/gitoxide/commit/feb259645651309b31df91b18ab247d6955f9a7f))
+ - Thanks clippy (I really tried) ([`e8880fb`](https://github.com/Byron/gitoxide/commit/e8880fb6e69c15bd44c21edb1ff6de4d8738e036))
+ - [clone] unbelievable, but it now seems to work as expected ([`88dbbf5`](https://github.com/Byron/gitoxide/commit/88dbbf5294446bfad1534d1c0635e9a5876ef769))
+ - [clone] quick hack to finish http set service, but something is seriously wrong… ([`dd93504`](https://github.com/Byron/gitoxide/commit/dd93504624f57e16b71119a8aadbe3aa8b971a01))
+ - [clone] non-deterministic behaviour when parsing HTML, despite ignoring the encoding ([`bab3ec3`](https://github.com/Byron/gitoxide/commit/bab3ec324ddbee50cfddfef76fd7715286fc9caf))
+ - [clone] It definitely doesn't want to read the data to the end with 'chunked' ([`49f1aca`](https://github.com/Byron/gitoxide/commit/49f1acac1817c02b76014965f0d26cdfaa4f062e))
+ - [clone] for good measure: configure posts (more) correctly ([`e491e58`](https://github.com/Byron/gitoxide/commit/e491e588eb2d3785ac7cc5a226733836e6552309))
+ - [clone] Disabling transfer decoding makes it better, but… ([`3a1b8bc`](https://github.com/Byron/gitoxide/commit/3a1b8bcfad37f8f5a0587d7f1a40a499f0c32131))
+ - [clone] It looks like curl is stumbling over the 'chunked' header ([`279a386`](https://github.com/Byron/gitoxide/commit/279a3868d1d8ac56bfb7db57a8fb9853bf992f7b))
+ - [clone] Fix deadlock - classic, and obvious ([`72a165e`](https://github.com/Byron/gitoxide/commit/72a165ea23b4ba119eb97bbe30912aa25b7502fe))
+ - [clone] possibly correct impl of Handler; still hangs though :D ([`aefd8d4`](https://github.com/Byron/gitoxide/commit/aefd8d459030ce3b88c7629894a36ace4ba0fb31))
+ - [clone] Fair enough - it locks up somewhere, let's see :D ([`33a1a22`](https://github.com/Byron/gitoxide/commit/33a1a2217e27121c8061ef9547c2589fed39e015))
+ - [clone] Improve usability of posts… ([`e1b944e`](https://github.com/Byron/gitoxide/commit/e1b944ef7d79294f56193431abade939e307c19a))
+ - [clone] Actually use that abstraction ([`d0bdbe4`](https://github.com/Byron/gitoxide/commit/d0bdbe42ed8a62f23643904f9d2284e31fdbabea))
+ - [clone] generalization of get and post ([`e62adc9`](https://github.com/Byron/gitoxide/commit/e62adc937d2c0256c15358e5ee6f76fc2cc5318f))
+ - [clone] Curl can now use remote to perform operations (get only for now) ([`a82f028`](https://github.com/Byron/gitoxide/commit/a82f0280f4240bb29801b1beabc96e6f7fa451d7))
+ - [clone] try sending curl error even harder… ([`b450bfc`](https://github.com/Byron/gitoxide/commit/b450bfc3d917d13dde9f32427dd905fb35d51063))
+ - [clone] first sketch of remote-curl, a way to transform curl into Read/Write ([`22b4b39`](https://github.com/Byron/gitoxide/commit/22b4b39a3e009d25bcfb18c33be1ca4dfcd76f2d))
+ - [clone] Send headers with BufReaders ([`6a95aaa`](https://github.com/Byron/gitoxide/commit/6a95aaab582941c6d1697dde6982c0aa8896c73d))
+ - Refactor ([`d427671`](https://github.com/Byron/gitoxide/commit/d4276719ac5e54bb70ee5a88c534acbf94e4a817))
+ - [clone] Fixed shortcomings of http error handling, with thiserror ([`424e159`](https://github.com/Byron/gitoxide/commit/424e1598a2b37f7b67250ad5b7ec62abdfe75f58))
+ - [clone] Allow to use specific HttpErrors, at the expense of source ([`b16a8c5`](https://github.com/Byron/gitoxide/commit/b16a8c5c153e7368f17c0a7110157f5a545a35ba))
+ - [clone] Fix 'small' compile (without http) ([`29ca5e8`](https://github.com/Byron/gitoxide/commit/29ca5e8df83beede03f966de2aeae5e30638f6bc))
+ - [clone] First step towards 'remote http executor' ([`f1e48d7`](https://github.com/Byron/gitoxide/commit/f1e48d7c178b15f0a0b9ba41fb4f94e4c6f0c74a))
+ - [clone] things get more complicated…take a step back maybe? ([`f778637`](https://github.com/Byron/gitoxide/commit/f77863726a5b7001e47f8f6b48eb0709a1cd0856))
+ - [clone] Right before actually performing the http call… ([`5bf9e6a`](https://github.com/Byron/gitoxide/commit/5bf9e6a21c1bdab3010e362604e324bcebbb45b0))
+ - [clone] better user agent header ([`4396587`](https://github.com/Byron/gitoxide/commit/4396587a2d1a0ca50119947048252709cafa277d))
+ - [clone] in small steps to getting the http 'interface' right ([`43f2a92`](https://github.com/Byron/gitoxide/commit/43f2a92c6f51a097f6f707953fe215c28c95fa04))
+ - [clone] A utility to respond with mock replies on a socket ([`1bf7ef7`](https://github.com/Byron/gitoxide/commit/1bf7ef739dc4c94b4be8a0748333c4486b6555b7))
+ - [clone] improvements to Http trait; prep for curl tests ([`9f69d6a`](https://github.com/Byron/gitoxide/commit/9f69d6ab3964b2cd566b5a6c4b739804d7cf3f7f))
+ - [clone] a piped iterator ([`5148c85`](https://github.com/Byron/gitoxide/commit/5148c85efc70c0ec06be3ebce267ce727c8ee4e1))
+ - Thanks clippy ([`c4f570f`](https://github.com/Byron/gitoxide/commit/c4f570fcae7e21745a37a4265b05d21e6149157b))
+ - [clone] frame for implementing 'pipe' support ([`c555681`](https://github.com/Byron/gitoxide/commit/c55568127ff943cc6749dba5054d7b3e93c049eb))
+ - Refactor ([`bfda633`](https://github.com/Byron/gitoxide/commit/bfda633234fa5661a34a98f9f8071570f6cea10c))
+ - [clone] sketch for http infrastructure to get going with curl ([`8351299`](https://github.com/Byron/gitoxide/commit/835129968b5414c980f24dc73aa89b43ac39bcaa))
+ - [clone] an easy way to get a few HTTP replies for consumption by the client ([`8b082d0`](https://github.com/Byron/gitoxide/commit/8b082d068f1adc0224ab5e0c646a91742dffaa5f))
+ - Refactor ([`0bbd87e`](https://github.com/Byron/gitoxide/commit/0bbd87e023da4e2cf0412e4c017816be11fc4174))
+ - Refactor ([`bbce340`](https://github.com/Byron/gitoxide/commit/bbce340e89bbc8b53b6632897ba5bf6dbdeafe11))
+ - Thanks clippy ([`73a6868`](https://github.com/Byron/gitoxide/commit/73a6868963993a3328e7d8fe94e5a6ac5078a944))
+ - [clone] Make it optional to abort the packet line reader on 'ERR <e>' ([`abf9c3b`](https://github.com/Byron/gitoxide/commit/abf9c3b3c9fe757a7418626cd985960f58718357))
+ - [clone] Finally it all works exactly as desired… ([`c5bbb57`](https://github.com/Byron/gitoxide/commit/c5bbb57ad7069c839757f72432d23c43de0b61da))
+ - [clone] Most of the V1 handshake works, but… ([`318024b`](https://github.com/Byron/gitoxide/commit/318024bc14b0bc88e7728bdacaa0c32265618f4d))
+ - [clone] YES! Boxes with dyn traits and lifetimes… ([`5e35d0a`](https://github.com/Byron/gitoxide/commit/5e35d0acf9efcb787eec0591c6f02735ae417e60))
+ - [clone] FAIL: Right, need a box after all ([`6e57927`](https://github.com/Byron/gitoxide/commit/6e5792746d164696a1b3bcb64f100328380e19df))
+ - [clone] FAIL: can't pass line reader as box ([`633341d`](https://github.com/Byron/gitoxide/commit/633341dd5f3fbd7b910c545e203e0bd734b5f989))
+ - [clone] sketching how to possibly return Line readers while keeping it sane… ([`4ba123b`](https://github.com/Byron/gitoxide/commit/4ba123b8e543a2ef3ba07aaf467b208047db0e1d))
+ - Thanks clippy ([`81c0185`](https://github.com/Byron/gitoxide/commit/81c0185bdb7f483021db1ab85064863c17f33571))
+ - Refactor ([`f8ff1c7`](https://github.com/Byron/gitoxide/commit/f8ff1c753300c3af7d328440a3591343cbe5f040))
+ - [clone] capability parsing ([`5b019af`](https://github.com/Byron/gitoxide/commit/5b019afe49fe1b35f72b91e4919f7a31e87f4f94))
+ - Refactor ([`2b40961`](https://github.com/Byron/gitoxide/commit/2b40961a2c653d1f5be7d31337e4eed8f08f900a))
+ - [clone] a little closer to handling the client handshake ([`1a4f84d`](https://github.com/Byron/gitoxide/commit/1a4f84dd698d89b83d251f81797cb4cf8c3ceb34))
+ - [clone] first frame for testing transport layer interactions ([`e1100c8`](https://github.com/Byron/gitoxide/commit/e1100c817bdf49d960aff7b7bee99302583d599c))
+ - Refactor ([`f3c5c05`](https://github.com/Byron/gitoxide/commit/f3c5c059169e9cc998ec0c80baf637142eb200ef))
+ - Bump git-features to 0.4 to allow publishes after breaking changes ([`9d6b879`](https://github.com/Byron/gitoxide/commit/9d6b8790e2edd7fa01b3239adff86a7cd2393f10))
+ - [clone] move packet-line code into own crate ([`879af67`](https://github.com/Byron/gitoxide/commit/879af671fcde405d3d08ddbc07ea70d0bee23ef1))
+ - [clone] http protocol is now optional ([`06c0816`](https://github.com/Byron/gitoxide/commit/06c0816bd0ed0ae0f125c1fa93ffe1b89e7e7eb1))
+ - [clone] (very) First stab at http protocol connection ([`218a5eb`](https://github.com/Byron/gitoxide/commit/218a5ebbd5c7b466406fd488ade6c60bde3f78a6))
+ - [clone] Better error handling for generalized `connect(…)` ([`713808c`](https://github.com/Byron/gitoxide/commit/713808cd8bd326b632c2b8f0cfbe7f147b1fa0aa))
+ - [clone] fix git-transport crate size ([`720f444`](https://github.com/Byron/gitoxide/commit/720f4442b06702b9efc1c257d2b54d4a22d3649d))
+ - [clone] enable git-transport tests ([`8e07be4`](https://github.com/Byron/gitoxide/commit/8e07be44ae38830f4894ed80ea9faab567593256))
+ - Refactor ([`104b7fe`](https://github.com/Byron/gitoxide/commit/104b7fef720751ba7306cee4010e90a20bd6955d))
+ - Thanks clippy ([`c62bfa2`](https://github.com/Byron/gitoxide/commit/c62bfa277b854ee21c6774ce88f086a2926dd858))
+ - [clone] expand-path should be server-side ([`8a38856`](https://github.com/Byron/gitoxide/commit/8a38856a811078d1d453db9c0e0ad7b6baaaed3c))
+ - [clone] the return of actually parsing remote progress ([`c465fde`](https://github.com/Byron/gitoxide/commit/c465fdecd4840627a3c6943af014999f5c9cc3e1))
+ - [clone] move packet-lint into transport layer ([`c0dd831`](https://github.com/Byron/gitoxide/commit/c0dd8315089243164d82c444499a459756a0337b))
+ - [clone] sample on how SSH connection fits in ([`a562059`](https://github.com/Byron/gitoxide/commit/a56205935ead562a8388e86565919081261dea2a))
+ - [clone] first sketch of transport layer's connection logic ([`f10cee5`](https://github.com/Byron/gitoxide/commit/f10cee5638a220fff629af274baebbcc0f4f0f61))
+ - Allow dual-licensing with Apache 2.0 ([`ea353eb`](https://github.com/Byron/gitoxide/commit/ea353eb02fd4f75508600cc5676107bc7e627f1e))
+ - Add missing license description ([`2b80181`](https://github.com/Byron/gitoxide/commit/2b80181ad428a9bf267a9660886f347a850fc76f))
+ - Make crates publishable ([`5688a34`](https://github.com/Byron/gitoxide/commit/5688a3427ff3673e1422d43106f4d685fa837aed))
+ - \#[forbid(unsafe)] for all crates ([`afda803`](https://github.com/Byron/gitoxide/commit/afda8039259b7a30cfed5dbcdd9caf4773b4c234))
+ - Cleanup - don't build and run tests while there is nothing to test ([`4a153da`](https://github.com/Byron/gitoxide/commit/4a153da0d60a30615fc402cfecb977f0d771594a))
+ - Prepare git-transport just so that we don't forget to take the name ([`2c3ad7d`](https://github.com/Byron/gitoxide/commit/2c3ad7d916ca513cc9dff26ff2150bae0dcb93e1))
+</details>
+
+## 0.25.3 (2023-01-10)
+
+A maintenance release without user-facing changes.
+
+## 0.25.2 (2023-01-09)
+
+### Bug Fixes
+
+ - <csr-id-6ba799c9d6b17ed665d3c352c3c4bb35c9f771bb/> `gix clone ssh://...` won't deadlock anymore.
+ For `cargo` specifically we now parse stderr to see if permission errors
+ occour. This links stderr and stdout and we have to pass information from
+ a supervisor thread that parses stderr to stdout and use the information to
+ return a custom io error in time.
+ Now the algorithm is adjusted to never be able to deadlock, as the problem
+ is inherently racy and somewhat hard to implement it properly especially without
+ a good test suite built-into `gitoxde` - there are no ssh servers one can easily
+ spin up cross-platform.
+
+## 0.25.1 (2022-12-31)
+
+### Bug Fixes
+
+ - <csr-id-ec2f2e31a714334bc0942eab08d306d4e0952933/> file:// command invocation won't spill stderr output.
+ This usually doesn't add any benefit to the user as we might see
+ events like the git process' failure to flush to a closed channel
+ even though this is entirely handled by the Rust side of things.
+
+ I can imagine that one day this might become a configurable to help
+ with debugging to help making better-behaved clients, but maybe it
+ won't ever matter once the default file:// transport is built-in and
+ native.
+
+## 0.25.0 (2022-12-30)
+
+### Bug Fixes
+
+ - <csr-id-fed38c90df546c4bfc57ef66c92b4c9312c90586/> improve error message for when an invoked transport program can't be found.
+ - <csr-id-f0997bfab2ba66fb12b0c9d4d673faeabda9687c/> assure processing thread is up before continuing.
+ That way one may hope that we never leave stderr output unprocessed.
+ - <csr-id-923278b4f245c31245a83f5f4d6e3b7dce8134e2/> port selections for SSH urls are now respected for protocol V1 as well.
+ - <csr-id-0ff127c62c2cc47b93ef4af108a382c62af1d3fb/> propery adjust `host` argument for `ssh` program to include a user name.
+ Otherwise it would not use a user at all which then defaults to the currently logged
+ in user, something that typically won't work with servers that demand `git`.
+
+### New Features (BREAKING)
+
+ - <csr-id-6fa27642aa57613cae82ae680f02923dad25d474/> ptions for `client::connect()` and support for more than one ssh variant, including permission-denied detection.
+ Options can be passed down to `client::ssh::connect()` to further configure it
+ similar to what git itself offers. That way, it's possible to use different ssh commands
+ and support all of gits configuration options.
+
+ Support for multiple ssh variants was added to use them (and their flags) correctly.
+
+ Detection of permission-denied errors due to invalid credentials was added so re-authentication
+ in upper layers can be implemented.
+
+## 0.24.2 (2022-12-26)
+
+### New Features
+
+ - <csr-id-d59d362f12bf617656bae80596120c8bf823b090/> add and implement various new http options for the `curl` backend.
+ - `schannel_check_revoke` as `curl`-backend specific configuration.
+
+### Bug Fixes
+
+ - <csr-id-c62e5c7d415351aefafeb75f0ab926c7c45c6ede/> fixes SSH clone from scp-like/relatives URLs
+ - Removes git-upload-pack extra parameters (rejected by both github and gitlab)
+
+## 0.24.1 (2022-12-22)
+
+A maintenance release without user-facing changes.
+
+## 0.24.0 (2022-12-19)
+
+### New Features
+
+ - <csr-id-0a2b135d19ce1f1b4b0394befaa3949906322c97/> improve granularity of IO errors for `curl` backends.
+ That way it should be possible to tell if `curl` caused
+ an IO error due to someting that can be considered spurious.
+ - <csr-id-9a2f7cd55c05f2fdb0ae62f0efca9dfa451694c7/> `IsSpuriousError` trait and its implementation.
+ That way all transports can tell if the operation failed due to
+ an issue that's probably passing, so retrying may be a way to resolve
+ the issue.
+ - <csr-id-5034544b36994177009ccc8d6c07cb000b429174/> `client::http::Options::no_proxy` to disable a proxy for given hosts.
+ This is a curl-first option which can reasonably be implemented for other backends
+ as well and thus retains its curl-ish roots in full.
+ - <csr-id-e701e7e9cc571108ca210fc0ca23494d6a1c7208/> `client::http::Options::verbose` to see more debug output.
+ This means different things depending on the backend, and for
+ `curl` it means a lot of debug-output on stderr.
+
+### Bug Fixes
+
+ - <csr-id-ff0332e815c228cc5cdfe58c3598ad261bb2879e/> http transports can now reuse a connection.
+ This makes connections more efficient generally and `cargo` relies
+ on that behaviour in their tests as well.
+ - <csr-id-85dcda81d3fec03ad5687b0e0329cefedd925722/> don't pre-configure curl.
+ These settings can interact strangly with other users of the curl package
+ within the same dependency tree, so it's paramount to only activate features
+ we truly need.
+ - <csr-id-0d0eb4aa46b265f97ada7b54d8bcc29decc42e50/> ssh connection remove '=' in port argument
+ - <csr-id-4927adf1a57166b581fc293a33f84ef628af70db/> make it possible to parse handshakes without newlines in packetlines #(639)
+ - <csr-id-7ab7c2409a47fae587531c0c3b203cd646e32984/> correctly display what's actual and expected when failing to parse capabilities.
+ - <csr-id-b0083e38c82829b4d8b81542fc8d1025089e2869/> improve compile-time errors if mutually exclusive http-client features are set.
+ - <csr-id-5f2276b63129163096be3cb229864fc589348da8/> don't enforce V2 as protocol, but smoothly downgrade like git does.
+ For backward compatibility the shared handshake implementation allows the
+ transport to control which protocol versions it wants to support
+ to allow optimizing for one special case, namely to prevent it to
+ read all V1 refs on old servers but abort instead, closing the connection
+ without delay.
+
+ Now we leave this feature for custom transports (who usually come with custom
+ servers) and instead support fallbacks to other protocols if the server
+ demands it.
+
+### New Features (BREAKING)
+
+ - <csr-id-1204bfcaadd31ed198b923df05f19115da3754a4/> Provide support for reading packetlines directly.
+ The handshake response itself now provides a `ref` read implementation
+ with direct readline support. That way one can avoid having to go
+ through `String`.
+ - <csr-id-8e158c3f4056f59724fe91587157ef0daa517964/> interpret the FollowRedirects option for the curl HTTP backend.
+ This comes with changes to the `HTTP` trait which now requires a base-url
+ to be provided as well.
+ - <csr-id-041eca547a6629c8540728eba95dbcd636285ba9/> make streaming otional for any reqwest.
+ One can now indicate when initiating a reqwest that the transport doesn't
+ have to stream the data, even though it will always be provided to an
+ `std::io::Write`.
+
+ Note that this is at the discretion of the transport implementation and streaming
+ might still be done despite it not being requested.
+
+ Note that the caller should set this 'streaming' flag if the upper bound of data
+ is high for keeping it in memory or can't be estimated. This is generally true
+ when sending packs.
+
+### Bug Fixes (BREAKING)
+
+ - <csr-id-08dcda254bc942dcc36d432cf7130c2ce4c6d54e/> `Capabiltiies::from_lines()` takes a single buffer.
+ That way it doesn't have to convert to `String` as intermediary
+ which may fail as illformed UTF8 might be present.
+
+ Performance wise, reading all lines ahead of time, it is the same
+ as it was before as it would collect all lines beforehand anyway.
+
+ We are also seeing only a few lines in V2, and in V1 it was
+ fully streaming already.
+
+## 0.23.0 (2022-11-21)
+
+### New Features
+
+ - <csr-id-68ed6d7e2cabc3d3bc78a29003863cd4194549fa/> Support for proxy authentication in http configuration.
+
+### 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-4308a209dddcbb461c34d45fb9af8b4621d4600a/> `max-pure` now builds without any C build tooling due to lack of `openssl-sys`.
+ To make this work, we leave the `reqwest` configuration to downstream crates.
+ Note that this means downstream will have to select their TLS settings
+ themselves, so builds may fail after upgrade until this is done.
+
+ An example for a `reqwest` configuration can be found in the
+ `gitoxide` Cargo.toml in the root of the `gitoxide` repository.
+
+## 0.22.0 (2022-11-17)
+
+### Changed
+
+ - <csr-id-28615b3bb9acff86d7a5520172513e3cc22aeda1/> `TransportV2Ext::invoke(…,features,…)` can take key-value pairs more flexibly.
+ Value can now also be owned, which is useful if the value type is a
+ `Cow<'_, String>`.
+
+### New Features
+
+ - <csr-id-42acc88bbc63850c0d38db70bc46b1058875e2a0/> `client::RequestWriter::into_parts()` to obtain a bare write handled along with a buf reader with packetline capabilties.
+ That way it's possible to perform interactions akin to a V1 puash.
+
+### Changed (BREAKING)
+
+ - <csr-id-07512db093e62d9b9185368bd3fa561cfcd1d1d2/> `client::TransportWithoutIO::to_url()` returns `Cow<'_, BStr>`.
+ That way, it's possible to efficiently return URLs in the right format,
+ or return generates ones as needed.
+ - <csr-id-fe2042bff9ae38bf76b76cef14986f9f76bded7d/> `client::TransportWithoutIO::to_url()` returns `BString`.
+ That way it will not be lossy in case the URL represents a path, which
+ is relevant for transports that refer to paths.
+
+ Note that this doesn't matter when the url actually is a URL, as it's
+ specified to be valid unicode (I think).
+ - <csr-id-759b5d482de048deb24d14043a173079914e7ac8/> `client::TransportV2Ext::invoke()` supports owned `capabilities`.
+ That way it's easier to pass custom agent strings when invoking.
+
+## 0.21.2 (2022-11-08)
+
+### Fixes
+
+- enable clones from `kernel.org` via `https`. This failed as it wouldn't send the service announcement in protocol V2,
+ which was thought to be mandatory at least when judging from `github.com`.
+
+## 0.21.1 (2022-11-06)
+
+### Bug Fixes
+
+ - <csr-id-375051fa97d79f95fa7179b536e616c4aefd88e2/> Allow `client::connect()` to function with `http-client-reqwest` enabled.
+ - <csr-id-4b5d6dfb58f325bba692e1e32636c24ba058022f/> `client::Capabilities` lifetimes now point to `'a` instead of `'self`.
+ This generally makes returned values longer, and as long as one would
+ expect.
+
+## 0.21.0 (2022-10-10)
+
+<csr-id-5bef0a00e8d01110c054a517f6d9696f981a7efc/>
+
+### New Features
+
+ - <csr-id-8e17534b0efa7418eabdc36f89bab9f9db7b2c38/> `reqwest` as blocking HTTP backend via `http-client-reqwest` feature toggle.
+ Note that despite passing the same tests that `curl` passes, for
+ bulletproof HTTP connections to untrusted servers, the `curl` backend
+ should be preferred. `reqwest` is known to hang if `content-length:`
+ HTTP headers are longer than the actual content, and no timeout is
+ kicking in to stop the hanging. `curl` has no trouble with this for
+ example.
+ - <csr-id-0fd57c6b491a3c8d0127bc1d1f0eb958437edff9/> Add `client::http::Transport::new_http()` constructor.
+ This goes along with `client::http::connect_http()` to support
+ connections to via custom transports which are passed from calling
+ crates, without relying on the implementation to be built-in.
+ - <csr-id-e05c1fefeed23dbedf0420e04a2a408510775380/> Allow defaulting `client::Capabilities`.
+ This can be useful in conjunction with `std::mem::take()`, even
+ though an empty data structure like that doesn't bear any significance
+ beyond that.
+
+### Bug Fixes
+
+ - <csr-id-41b0c19e1aca9406015932862058756af2a26dda/> set the protocol version for local git transports as well.
+ Previously this was only done for ssh based connections, and requires
+ setting an environment variable.
+ - <csr-id-6d8b66a9bee901eb8cb869e6e28dbb25988f1fed/> remove `Drop` for `SpawnProcessOnDemand`.
+ It is well-intended but is likely to hang the calling process
+ if for any reason not all process output was consumed.
+
+ Even though not waiting for the process leaves it running, it will
+ stop naturally once its output pipe breaks once once
+ our handles for it are inevitable dropped at the same time.
+ - <csr-id-237682a529dc54e33e4738f34915d872aeb89514/> compare 'Content-Type' header case-insensitively, as required by the http spec.
+
+### Other
+
+ - <csr-id-5bef0a00e8d01110c054a517f6d9696f981a7efc/> try to make the transport configurable after being boxed, but…
+ …that would force it to be 'static, which is something we explicitly
+ cannot have. We need references to be contained within, if I remember
+ correctly.
+
+### Changed (BREAKING)
+
+ - <csr-id-1cf66c4dbc7a0404701efe4335363c2636ce32f8/> `client::http::connect()` returns `Transport<Impl>` directly.
+ It' can't fail, so no need to return `Result<_, Infallible>`.
+
+### New Features (BREAKING)
+
+ - <csr-id-78ad3df64f2c016ba17b158bd9ab1d2341aab399/> add `fetch::Transport::configure` to generically configure any transport.
+
+## 0.20.0 (2022-09-20)
+
+### Changed (BREAKING)
+
+ - <csr-id-99905bacace8aed42b16d43f0f04cae996cb971c/> upgrade `bstr` to `1.0.1`
+
+## 0.19.3 (2022-08-28)
+
+Maintenance release without user-facing changes.
+
+## 0.19.2 (2022-08-24)
+
+<csr-id-f7f136dbe4f86e7dee1d54835c420ec07c96cd78/>
+<csr-id-533e887e80c5f7ede8392884562e1c5ba56fb9a8/>
+
+### Chore
+
+ - <csr-id-f7f136dbe4f86e7dee1d54835c420ec07c96cd78/> uniformize deny attributes
+ - <csr-id-533e887e80c5f7ede8392884562e1c5ba56fb9a8/> remove default link to cargo doc everywhere
+
+### 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
+
+### Bug Fixes
+
+ - <csr-id-5220f9a59fb699e111342b076145a6899d36d433/> Make async `conenct()` signature compatible with the blocking implementation.
+
+## 0.19.1 (2022-08-17)
+
+A maintenance release without user facing changes.
+
+### Changed (BREAKING)
+
+ - <csr-id-9509ce4faeca8b4e1527bac625370403495bb03c/> `client::connect()` supports anything that parses into a `gix_url::Url`; turn http url back to &str
+ The http url is always valid UTF-8 and doesn't contain invalid paths,
+ thus we should have the type system reflect that.
+ - <csr-id-52e8c149ff17ce894cc30d03ead1988f52f0663e/> `client::connect()` now takes a `&BStr` as URL
+ - <csr-id-71a43d0bc12661efcb9c94697c704f700a3be488/> use `thiserror` instead of `quickerror`
+ - <csr-id-12589cc6f08e4d7aabae30bcdadaa0c2b4850229/> adapt to changes in `gix-url` and use `BString` to represent URLs.
+ They can contain paths, which is why `String` can't represent a URL
+ losslessly.
+
+ For HTTP urls these are ultimately UTF-8 strings though.
+
+### New Features
+
+ - <csr-id-f6a6a499f20e12e2bcca734bdf3c8599d37f6a6f/> `connect()` method is available in when `async-std` feature is set along with `async-client`
+ This makes some async support available even trough the base crate,
+ which otherwise would require establishing a connection (with a runtime
+ of choice) by hand.
+
+## 0.19.0 (2022-07-22)
+
+This is a maintenance release with no functional changes.
+
+## 0.18.0 (2022-06-13)
+
+A maintenance release without user-facing changes.
+
+## 0.17.0 (2022-05-18)
+
+### New Features (BREAKING)
+
+ - <csr-id-32dc1829a5661f66396d109c8d0a8eaae6b1f532/> use `gix-credentials` in `gix-protocol`
+
+## 0.16.0 (2022-04-03)
+
+### New Features
+
+ - <csr-id-39778fd76191cfdb60df87eab8da59e575e48c78/> in-manifest and in-lib documentation of feature toggles
+
+## 0.15.0 (2022-01-23)
+
+A maintenance release with no relevant changes.
+
+## 0.14.0 (2021-11-29)
+
+A maintenance release, triggered by putting too many adjustments into a single commit.
+
+## 0.13.1 (2021-11-16)
+
+A maintenance release triggered by changes to gix-pack and changelog rewrites.
+
+## v0.13.0 (2021-10-19)
+
+A maintenance release due to properly dealing with previously breaking changes in `gix-hash`.
+
+## v0.12.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
+
+* `gix-packetline` is now publicly re-exported as `packetline`.
+
+ This helps to avoid additional dependencies which in turn may have breaking
+ changes. Re-using `packetline` assures it's usable.
+
+## v0.11.1 (2021-08-29)
+
+- instruct docs.rs which features to use for more useful documentation
+
+## v0.11.0 (2021-08-27)
+
+## v0.10.1 (2021-08-17)
+
+## v0.10.0 (2021-08-13)
+
+## v0.9.0 (2021-08-10)
+
+<csr-id-2f3725efcaa439db4e10ade1b9fbeb1258fd93c1/>
+
+### Other
+
+ - <csr-id-2f3725efcaa439db4e10ade1b9fbeb1258fd93c1/> make capabilities parsing public
+
+## v0.8.0 (2021-05-09)
+
+## v0.7.0 (2021-04-08)
+
+## v0.6.0 (2021-03-26)
+
+## v0.5.1 (2021-01-05)
+
+## v0.5.0 (2020-12-16)
+
+## v0.4.0 (2020-12-15)
+
+## v0.3.0 (2020-12-15)
+
+## v0.2.1 (2020-09-14)
+
+## v0.2.0 (2020-09-12)
+
+## v0.0.0 (2020-07-12)
+
diff --git a/vendor/gix-transport/Cargo.toml b/vendor/gix-transport/Cargo.toml
new file mode 100644
index 000000000..c4451d414
--- /dev/null
+++ b/vendor/gix-transport/Cargo.toml
@@ -0,0 +1,175 @@
+# 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-transport"
+version = "0.27.0"
+authors = ["Sebastian Thiel <sebastian.thiel@icloud.com>"]
+include = [
+ "src/**/*",
+ "CHANGELOG.md",
+]
+description = "A WIP crate of the gitoxide project dedicated to implementing the git transport layer"
+license = "MIT/Apache-2.0"
+repository = "https://github.com/Byron/gitoxide"
+
+[package.metadata.docs.rs]
+features = [
+ "http-client-curl",
+ "document-features",
+ "serde1",
+]
+rustdoc-args = [
+ "--cfg",
+ "docsrs",
+]
+
+[lib]
+doctest = false
+
+[[test]]
+name = "blocking-transport"
+path = "tests/blocking-transport.rs"
+required-features = [
+ "blocking-client",
+ "maybe-async/is_sync",
+]
+
+[[test]]
+name = "blocking-transport-http-only"
+path = "tests/blocking-transport-http.rs"
+required-features = [
+ "http-client-curl",
+ "maybe-async/is_sync",
+]
+
+[[test]]
+name = "async-transport"
+path = "tests/async-transport.rs"
+required-features = ["async-client"]
+
+[dependencies.async-std]
+version = "1.12.0"
+optional = true
+
+[dependencies.async-trait]
+version = "0.1.51"
+optional = true
+
+[dependencies.base64]
+version = "0.21.0"
+optional = true
+
+[dependencies.bstr]
+version = "1.3.0"
+features = [
+ "std",
+ "unicode",
+]
+default-features = false
+
+[dependencies.curl]
+version = "0.4"
+optional = true
+
+[dependencies.document-features]
+version = "0.2.0"
+optional = true
+
+[dependencies.futures-io]
+version = "0.3.16"
+optional = true
+
+[dependencies.futures-lite]
+version = "1.12.0"
+optional = true
+
+[dependencies.gix-command]
+version = "^0.2.4"
+
+[dependencies.gix-credentials]
+version = "^0.11.0"
+optional = true
+
+[dependencies.gix-features]
+version = "^0.28.0"
+
+[dependencies.gix-packetline]
+version = "^0.14.3"
+
+[dependencies.gix-quote]
+version = "^0.4.3"
+
+[dependencies.gix-sec]
+version = "^0.6.2"
+
+[dependencies.gix-url]
+version = "^0.15.0"
+
+[dependencies.pin-project-lite]
+version = "0.2.6"
+optional = true
+
+[dependencies.reqwest]
+version = "0.11.12"
+features = ["blocking"]
+optional = true
+default-features = false
+
+[dependencies.serde]
+version = "1.0.114"
+features = [
+ "std",
+ "derive",
+]
+optional = true
+default-features = false
+
+[dependencies.thiserror]
+version = "1.0.26"
+
+[dev-dependencies.async-std]
+version = "1.9.0"
+features = ["attributes"]
+
+[dev-dependencies.blocking]
+version = "1.0.2"
+
+[dev-dependencies.maybe-async]
+version = "0.2.6"
+
+[features]
+async-client = [
+ "gix-packetline/async-io",
+ "async-trait",
+ "futures-lite",
+ "futures-io",
+ "pin-project-lite",
+]
+blocking-client = ["gix-packetline/blocking-io"]
+default = []
+http-client = [
+ "base64",
+ "gix-features/io-pipe",
+ "blocking-client",
+ "gix-credentials",
+]
+http-client-curl = [
+ "curl",
+ "http-client",
+]
+http-client-reqwest = [
+ "reqwest",
+ "http-client",
+]
+serde1 = ["serde"]
diff --git a/vendor/gix-transport/src/client/async_io/bufread_ext.rs b/vendor/gix-transport/src/client/async_io/bufread_ext.rs
new file mode 100644
index 000000000..abe206b8a
--- /dev/null
+++ b/vendor/gix-transport/src/client/async_io/bufread_ext.rs
@@ -0,0 +1,126 @@
+use std::{
+ io,
+ ops::{Deref, DerefMut},
+};
+
+use async_trait::async_trait;
+use futures_io::{AsyncBufRead, AsyncRead};
+use gix_packetline::PacketLineRef;
+
+use crate::{
+ client::{Error, MessageKind},
+ Protocol,
+};
+
+/// A function `f(is_error, text)` receiving progress or error information.
+/// As it is not a future itself, it must not block. If IO is performed within the function, be sure to spawn
+/// it onto an executor.
+pub type HandleProgress = Box<dyn FnMut(bool, &[u8])>;
+
+/// This trait exists to get a version of a `gix_packetline::Provider` without type parameters,
+/// but leave support for reading lines directly without forcing them through `String`.
+///
+/// For the sake of usability, it also implements [`std::io::BufRead`] making it trivial to
+/// read pack files while keeping open the option to read individual lines with low overhead.
+#[async_trait(?Send)]
+pub trait ReadlineBufRead: AsyncBufRead {
+ /// Read a packet line into the internal buffer and return it.
+ ///
+ /// Returns `None` if the end of iteration is reached because of one of the following:
+ ///
+ /// * natural EOF
+ /// * ERR packet line encountered
+ /// * A `delimiter` packet line encountered
+ async fn readline(
+ &mut self,
+ ) -> Option<io::Result<Result<gix_packetline::PacketLineRef<'_>, gix_packetline::decode::Error>>>;
+}
+
+/// Provide even more access to the underlying packet reader.
+#[async_trait(?Send)]
+pub trait ExtendedBufRead: ReadlineBufRead {
+ /// Set the handler to which progress will be delivered.
+ ///
+ /// Note that this is only possible if packet lines are sent in side band mode.
+ fn set_progress_handler(&mut self, handle_progress: Option<HandleProgress>);
+ /// Peek the next data packet line. Maybe None if the next line is a packet we stop at, queryable using
+ /// [`stopped_at()`][ExtendedBufRead::stopped_at()].
+ async fn peek_data_line(&mut self) -> Option<io::Result<Result<&[u8], Error>>>;
+ /// Resets the reader to allow reading past a previous stop, and sets delimiters according to the
+ /// given protocol.
+ fn reset(&mut self, version: Protocol);
+ /// Return the kind of message at which the reader stopped.
+ fn stopped_at(&self) -> Option<MessageKind>;
+}
+
+#[async_trait(?Send)]
+impl<'a, T: ReadlineBufRead + ?Sized + 'a + Unpin> ReadlineBufRead for Box<T> {
+ async fn readline(&mut self) -> Option<io::Result<Result<PacketLineRef<'_>, gix_packetline::decode::Error>>> {
+ self.deref_mut().readline().await
+ }
+}
+
+#[async_trait(?Send)]
+impl<'a, T: ExtendedBufRead + ?Sized + 'a + Unpin> ExtendedBufRead for Box<T> {
+ fn set_progress_handler(&mut self, handle_progress: Option<HandleProgress>) {
+ self.deref_mut().set_progress_handler(handle_progress)
+ }
+
+ async fn peek_data_line(&mut self) -> Option<io::Result<Result<&[u8], Error>>> {
+ self.deref_mut().peek_data_line().await
+ }
+
+ fn reset(&mut self, version: Protocol) {
+ self.deref_mut().reset(version)
+ }
+
+ fn stopped_at(&self) -> Option<MessageKind> {
+ self.deref().stopped_at()
+ }
+}
+
+#[async_trait(?Send)]
+impl<T: AsyncRead + Unpin> ReadlineBufRead for gix_packetline::read::WithSidebands<'_, T, for<'b> fn(bool, &'b [u8])> {
+ async fn readline(&mut self) -> Option<io::Result<Result<PacketLineRef<'_>, gix_packetline::decode::Error>>> {
+ self.read_data_line().await
+ }
+}
+
+#[async_trait(?Send)]
+impl<'a, T: AsyncRead + Unpin> ReadlineBufRead for gix_packetline::read::WithSidebands<'a, T, HandleProgress> {
+ async fn readline(&mut self) -> Option<io::Result<Result<PacketLineRef<'_>, gix_packetline::decode::Error>>> {
+ self.read_data_line().await
+ }
+}
+
+#[async_trait(?Send)]
+impl<'a, T: AsyncRead + Unpin> ExtendedBufRead for gix_packetline::read::WithSidebands<'a, T, HandleProgress> {
+ fn set_progress_handler(&mut self, handle_progress: Option<HandleProgress>) {
+ self.set_progress_handler(handle_progress)
+ }
+ async fn peek_data_line(&mut self) -> Option<io::Result<Result<&[u8], Error>>> {
+ match self.peek_data_line().await {
+ Some(Ok(Ok(line))) => Some(Ok(Ok(line))),
+ Some(Ok(Err(err))) => Some(Ok(Err(err.into()))),
+ Some(Err(err)) => Some(Err(err)),
+ None => None,
+ }
+ }
+ fn reset(&mut self, version: Protocol) {
+ match version {
+ Protocol::V1 => self.reset_with(&[gix_packetline::PacketLineRef::Flush]),
+ Protocol::V2 => self.reset_with(&[
+ gix_packetline::PacketLineRef::Delimiter,
+ gix_packetline::PacketLineRef::Flush,
+ ]),
+ }
+ }
+ fn stopped_at(&self) -> Option<MessageKind> {
+ self.stopped_at().map(|l| match l {
+ gix_packetline::PacketLineRef::Flush => MessageKind::Flush,
+ gix_packetline::PacketLineRef::Delimiter => MessageKind::Delimiter,
+ gix_packetline::PacketLineRef::ResponseEnd => MessageKind::ResponseEnd,
+ gix_packetline::PacketLineRef::Data(_) => unreachable!("data cannot be a delimiter"),
+ })
+ }
+}
diff --git a/vendor/gix-transport/src/client/async_io/connect.rs b/vendor/gix-transport/src/client/async_io/connect.rs
new file mode 100644
index 000000000..fe2a5808e
--- /dev/null
+++ b/vendor/gix-transport/src/client/async_io/connect.rs
@@ -0,0 +1,47 @@
+pub use crate::client::non_io_types::connect::{Error, Options};
+
+#[cfg(any(feature = "async-std"))]
+pub(crate) mod function {
+ use std::convert::TryInto;
+
+ use crate::client::{git, non_io_types::connect::Error};
+
+ /// A general purpose connector connecting to a repository identified by the given `url`.
+ ///
+ /// This includes connections to
+ /// [git daemons][crate::client::git::connect()] only at the moment.
+ ///
+ /// Use `options` to further control specifics of the transport resulting from the connection.
+ pub async fn connect<Url, E>(
+ url: Url,
+ options: super::Options,
+ ) -> Result<Box<dyn crate::client::Transport + Send>, Error>
+ where
+ Url: TryInto<gix_url::Url, Error = E>,
+ gix_url::parse::Error: From<E>,
+ {
+ let mut url = url.try_into().map_err(gix_url::parse::Error::from)?;
+ Ok(match url.scheme {
+ gix_url::Scheme::Git => {
+ if url.user().is_some() {
+ return Err(Error::UnsupportedUrlTokens {
+ url: url.to_bstring(),
+ scheme: url.scheme,
+ });
+ }
+ let path = std::mem::take(&mut url.path);
+ Box::new(
+ git::Connection::new_tcp(
+ url.host().expect("host is present in url"),
+ url.port,
+ path,
+ options.version,
+ )
+ .await
+ .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?,
+ )
+ }
+ scheme => return Err(Error::UnsupportedScheme(scheme)),
+ })
+ }
+}
diff --git a/vendor/gix-transport/src/client/async_io/mod.rs b/vendor/gix-transport/src/client/async_io/mod.rs
new file mode 100644
index 000000000..6cb1a500e
--- /dev/null
+++ b/vendor/gix-transport/src/client/async_io/mod.rs
@@ -0,0 +1,13 @@
+mod bufread_ext;
+pub use bufread_ext::{ExtendedBufRead, HandleProgress, ReadlineBufRead};
+
+mod request;
+pub use request::RequestWriter;
+
+mod traits;
+pub use traits::{SetServiceResponse, Transport, TransportV2Ext};
+
+///
+pub mod connect;
+#[cfg(any(feature = "async-std"))]
+pub use connect::function::connect;
diff --git a/vendor/gix-transport/src/client/async_io/request.rs b/vendor/gix-transport/src/client/async_io/request.rs
new file mode 100644
index 000000000..1121db356
--- /dev/null
+++ b/vendor/gix-transport/src/client/async_io/request.rs
@@ -0,0 +1,103 @@
+use std::{
+ io,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+use futures_io::AsyncWrite;
+use pin_project_lite::pin_project;
+
+use crate::client::{ExtendedBufRead, MessageKind, WriteMode};
+
+pin_project! {
+ /// A [`Write`][io::Write] implementation optimized for writing packet lines.
+ /// A type implementing `Write` for packet lines, which when done can be transformed into a `Read` for
+ /// obtaining the response.
+ pub struct RequestWriter<'a> {
+ on_into_read: MessageKind,
+ #[pin]
+ writer: gix_packetline::Writer<Box<dyn AsyncWrite + Unpin + 'a>>,
+ reader: Box<dyn ExtendedBufRead + Unpin + 'a>,
+ }
+}
+impl<'a> futures_io::AsyncWrite for RequestWriter<'a> {
+ fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>> {
+ self.project().writer.poll_write(cx, buf)
+ }
+
+ fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+ self.project().writer.poll_flush(cx)
+ }
+
+ fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+ self.project().writer.poll_close(cx)
+ }
+}
+
+/// methods with bonds to IO
+impl<'a> RequestWriter<'a> {
+ /// Create a new instance from a `writer` (commonly a socket), a `reader` into which to transform once the
+ /// writes are finished, along with configuration for the `write_mode` and information about which message to write
+ /// when this instance is converted [into a `reader`][RequestWriter::into_read()] to read the request's response.
+ pub fn new_from_bufread<W: AsyncWrite + Unpin + 'a>(
+ writer: W,
+ reader: Box<dyn ExtendedBufRead + Unpin + 'a>,
+ write_mode: WriteMode,
+ on_into_read: MessageKind,
+ ) -> Self {
+ let mut writer = gix_packetline::Writer::new(Box::new(writer) as Box<dyn AsyncWrite + Unpin>);
+ match write_mode {
+ WriteMode::Binary => writer.enable_binary_mode(),
+ WriteMode::OneLfTerminatedLinePerWriteCall => writer.enable_text_mode(),
+ }
+ RequestWriter {
+ on_into_read,
+ writer,
+ reader,
+ }
+ }
+
+ /// Write the given message as packet line.
+ pub async fn write_message(&mut self, message: MessageKind) -> io::Result<()> {
+ match message {
+ MessageKind::Flush => {
+ gix_packetline::PacketLineRef::Flush
+ .write_to(self.writer.inner_mut())
+ .await
+ }
+ MessageKind::Delimiter => {
+ gix_packetline::PacketLineRef::Delimiter
+ .write_to(self.writer.inner_mut())
+ .await
+ }
+ MessageKind::ResponseEnd => {
+ gix_packetline::PacketLineRef::ResponseEnd
+ .write_to(self.writer.inner_mut())
+ .await
+ }
+ MessageKind::Text(t) => gix_packetline::TextRef::from(t).write_to(self.writer.inner_mut()).await,
+ }
+ .map(|_| ())
+ }
+ /// Discard the ability to write and turn this instance into the reader for obtaining the other side's response.
+ ///
+ /// Doing so will also write the message type this instance was initialized with.
+ pub async fn into_read(mut self) -> std::io::Result<Box<dyn ExtendedBufRead + Unpin + 'a>> {
+ self.write_message(self.on_into_read).await?;
+ Ok(self.reader)
+ }
+
+ /// Dissolve this instance into its write and read handles without any message-writing side-effect as in [RequestWriter::into_read()].
+ ///
+ /// Furthermore, the writer will not encode everything it writes as packetlines, but write everything verbatim into the
+ /// underlying channel.
+ ///
+ /// # Note
+ ///
+ /// It's of utmost importance to drop the request writer before reading the response as these might be inter-dependent, depending on
+ /// the underlying transport mechanism. Failure to do so may result in a deadlock depending on how the write and read mechanism
+ /// is implemented.
+ pub fn into_parts(self) -> (Box<dyn AsyncWrite + Unpin + 'a>, Box<dyn ExtendedBufRead + Unpin + 'a>) {
+ (self.writer.into_inner(), self.reader)
+ }
+}
diff --git a/vendor/gix-transport/src/client/async_io/traits.rs b/vendor/gix-transport/src/client/async_io/traits.rs
new file mode 100644
index 000000000..ea73f5e09
--- /dev/null
+++ b/vendor/gix-transport/src/client/async_io/traits.rs
@@ -0,0 +1,113 @@
+use std::ops::DerefMut;
+
+use async_trait::async_trait;
+use bstr::BString;
+use futures_lite::io::AsyncWriteExt;
+
+use crate::{
+ client::{Capabilities, Error, ExtendedBufRead, MessageKind, TransportWithoutIO, WriteMode},
+ Protocol, Service,
+};
+
+/// The response of the [`handshake()`][Transport::handshake()] method.
+pub struct SetServiceResponse<'a> {
+ /// The protocol the service can provide. May be different from the requested one
+ pub actual_protocol: Protocol,
+ /// The capabilities parsed from the server response.
+ pub capabilities: Capabilities,
+ /// In protocol version one, this is set to a list of refs and their peeled counterparts.
+ pub refs: Option<Box<dyn crate::client::ReadlineBufRead + Unpin + 'a>>,
+}
+
+/// All methods provided here must be called in the correct order according to the [communication protocol][Protocol]
+/// used to connect to them.
+/// It does, however, know just enough to be able to provide a higher-level interface than would otherwise be possible.
+/// Thus the consumer of this trait will not have to deal with packet lines at all.
+/// **Note that** whenever a `Read` trait or `Write` trait is produced, it must be exhausted.
+#[async_trait(?Send)]
+pub trait Transport: TransportWithoutIO {
+ /// Initiate connection to the given service and send the given `extra_parameters` along with it.
+ ///
+ /// `extra_parameters` are interpreted as `key=value` pairs if the second parameter is `Some` or as `key`
+ /// if it is None.
+ ///
+ /// Returns the service capabilities according according to the actual [Protocol] it supports,
+ /// and possibly a list of refs to be obtained.
+ /// This means that asking for an unsupported protocol might result in a protocol downgrade to the given one
+ /// if [TransportWithoutIO::supported_protocol_versions()] includes it or is empty.
+ /// Exhaust the returned [BufReader][SetServiceResponse::refs] for a list of references in case of protocol V1
+ /// before making another request.
+ async fn handshake<'a>(
+ &mut self,
+ service: Service,
+ extra_parameters: &'a [(&'a str, Option<&'a str>)],
+ ) -> Result<SetServiceResponse<'_>, Error>;
+}
+
+// Would be nice if the box implementation could auto-forward to all implemented traits.
+#[async_trait(?Send)]
+impl<T: Transport + ?Sized> Transport for Box<T> {
+ async fn handshake<'a>(
+ &mut self,
+ service: Service,
+ extra_parameters: &'a [(&'a str, Option<&'a str>)],
+ ) -> Result<SetServiceResponse<'_>, Error> {
+ self.deref_mut().handshake(service, extra_parameters).await
+ }
+}
+
+// Would be nice if the box implementation could auto-forward to all implemented traits.
+#[async_trait(?Send)]
+impl<T: Transport + ?Sized> Transport for &mut T {
+ async fn handshake<'a>(
+ &mut self,
+ service: Service,
+ extra_parameters: &'a [(&'a str, Option<&'a str>)],
+ ) -> Result<SetServiceResponse<'_>, Error> {
+ self.deref_mut().handshake(service, extra_parameters).await
+ }
+}
+
+/// An extension trait to add more methods to everything implementing [`Transport`].
+#[async_trait(?Send)]
+pub trait TransportV2Ext {
+ /// Invoke a protocol V2 style `command` with given `capabilities` and optional command specific `arguments`.
+ /// The `capabilities` were communicated during the handshake.
+ /// _Note:_ panics if [handshake][Transport::handshake()] wasn't performed beforehand.
+ async fn invoke<'a>(
+ &mut self,
+ command: &str,
+ capabilities: impl Iterator<Item = (&'a str, Option<impl AsRef<str>>)> + 'a,
+ arguments: Option<impl Iterator<Item = bstr::BString> + 'a>,
+ ) -> Result<Box<dyn ExtendedBufRead + Unpin + '_>, Error>;
+}
+
+#[async_trait(?Send)]
+impl<T: Transport> TransportV2Ext for T {
+ async fn invoke<'a>(
+ &mut self,
+ command: &str,
+ capabilities: impl Iterator<Item = (&'a str, Option<impl AsRef<str>>)> + 'a,
+ arguments: Option<impl Iterator<Item = BString> + 'a>,
+ ) -> Result<Box<dyn ExtendedBufRead + Unpin + '_>, Error> {
+ let mut writer = self.request(WriteMode::OneLfTerminatedLinePerWriteCall, MessageKind::Flush)?;
+ writer.write_all(format!("command={}", command).as_bytes()).await?;
+ for (name, value) in capabilities {
+ match value {
+ Some(value) => {
+ writer
+ .write_all(format!("{}={}", name, value.as_ref()).as_bytes())
+ .await
+ }
+ None => writer.write_all(name.as_bytes()).await,
+ }?;
+ }
+ if let Some(arguments) = arguments {
+ writer.write_message(MessageKind::Delimiter).await?;
+ for argument in arguments {
+ writer.write_all(argument.as_ref()).await?;
+ }
+ }
+ Ok(writer.into_read().await?)
+ }
+}
diff --git a/vendor/gix-transport/src/client/blocking_io/bufread_ext.rs b/vendor/gix-transport/src/client/blocking_io/bufread_ext.rs
new file mode 100644
index 000000000..5842ddd3d
--- /dev/null
+++ b/vendor/gix-transport/src/client/blocking_io/bufread_ext.rs
@@ -0,0 +1,114 @@
+use std::{
+ io,
+ ops::{Deref, DerefMut},
+};
+
+use gix_packetline::PacketLineRef;
+
+use crate::{
+ client::{Error, MessageKind},
+ Protocol,
+};
+/// A function `f(is_error, text)` receiving progress or error information.
+pub type HandleProgress = Box<dyn FnMut(bool, &[u8])>;
+
+/// This trait exists to get a version of a `gix_packetline::Provider` without type parameters,
+/// but leave support for reading lines directly without forcing them through `String`.
+///
+/// For the sake of usability, it also implements [`std::io::BufRead`] making it trivial to
+/// read pack files while keeping open the option to read individual lines with low overhead.
+pub trait ReadlineBufRead: io::BufRead {
+ /// Read a packet line into the internal buffer and return it.
+ ///
+ /// Returns `None` if the end of iteration is reached because of one of the following:
+ ///
+ /// * natural EOF
+ /// * ERR packet line encountered
+ /// * A `delimiter` packet line encountered
+ fn readline(
+ &mut self,
+ ) -> Option<io::Result<Result<gix_packetline::PacketLineRef<'_>, gix_packetline::decode::Error>>>;
+}
+
+/// Provide even more access to the underlying packet reader.
+pub trait ExtendedBufRead: ReadlineBufRead {
+ /// Set the handler to which progress will be delivered.
+ ///
+ /// Note that this is only possible if packet lines are sent in side band mode.
+ fn set_progress_handler(&mut self, handle_progress: Option<HandleProgress>);
+ /// Peek the next data packet line. Maybe None if the next line is a packet we stop at, queryable using
+ /// [`stopped_at()`][ExtendedBufRead::stopped_at()].
+ fn peek_data_line(&mut self) -> Option<io::Result<Result<&[u8], Error>>>;
+ /// Resets the reader to allow reading past a previous stop, and sets delimiters according to the
+ /// given protocol.
+ fn reset(&mut self, version: Protocol);
+ /// Return the kind of message at which the reader stopped.
+ fn stopped_at(&self) -> Option<MessageKind>;
+}
+
+impl<'a, T: ReadlineBufRead + ?Sized + 'a> ReadlineBufRead for Box<T> {
+ fn readline(&mut self) -> Option<io::Result<Result<PacketLineRef<'_>, gix_packetline::decode::Error>>> {
+ ReadlineBufRead::readline(self.deref_mut())
+ }
+}
+
+impl<'a, T: ExtendedBufRead + ?Sized + 'a> ExtendedBufRead for Box<T> {
+ fn set_progress_handler(&mut self, handle_progress: Option<HandleProgress>) {
+ self.deref_mut().set_progress_handler(handle_progress)
+ }
+
+ fn peek_data_line(&mut self) -> Option<io::Result<Result<&[u8], Error>>> {
+ self.deref_mut().peek_data_line()
+ }
+
+ fn reset(&mut self, version: Protocol) {
+ self.deref_mut().reset(version)
+ }
+
+ fn stopped_at(&self) -> Option<MessageKind> {
+ self.deref().stopped_at()
+ }
+}
+
+impl<T: io::Read> ReadlineBufRead for gix_packetline::read::WithSidebands<'_, T, fn(bool, &[u8])> {
+ fn readline(&mut self) -> Option<io::Result<Result<PacketLineRef<'_>, gix_packetline::decode::Error>>> {
+ self.read_data_line()
+ }
+}
+
+impl<'a, T: io::Read> ReadlineBufRead for gix_packetline::read::WithSidebands<'a, T, HandleProgress> {
+ fn readline(&mut self) -> Option<io::Result<Result<PacketLineRef<'_>, gix_packetline::decode::Error>>> {
+ self.read_data_line()
+ }
+}
+
+impl<'a, T: io::Read> ExtendedBufRead for gix_packetline::read::WithSidebands<'a, T, HandleProgress> {
+ fn set_progress_handler(&mut self, handle_progress: Option<HandleProgress>) {
+ self.set_progress_handler(handle_progress)
+ }
+ fn peek_data_line(&mut self) -> Option<io::Result<Result<&[u8], Error>>> {
+ match self.peek_data_line() {
+ Some(Ok(Ok(line))) => Some(Ok(Ok(line))),
+ Some(Ok(Err(err))) => Some(Ok(Err(err.into()))),
+ Some(Err(err)) => Some(Err(err)),
+ None => None,
+ }
+ }
+ fn reset(&mut self, version: Protocol) {
+ match version {
+ Protocol::V1 => self.reset_with(&[gix_packetline::PacketLineRef::Flush]),
+ Protocol::V2 => self.reset_with(&[
+ gix_packetline::PacketLineRef::Delimiter,
+ gix_packetline::PacketLineRef::Flush,
+ ]),
+ }
+ }
+ fn stopped_at(&self) -> Option<MessageKind> {
+ self.stopped_at().map(|l| match l {
+ gix_packetline::PacketLineRef::Flush => MessageKind::Flush,
+ gix_packetline::PacketLineRef::Delimiter => MessageKind::Delimiter,
+ gix_packetline::PacketLineRef::ResponseEnd => MessageKind::ResponseEnd,
+ gix_packetline::PacketLineRef::Data(_) => unreachable!("data cannot be a delimiter"),
+ })
+ }
+}
diff --git a/vendor/gix-transport/src/client/blocking_io/connect.rs b/vendor/gix-transport/src/client/blocking_io/connect.rs
new file mode 100644
index 000000000..de3334f18
--- /dev/null
+++ b/vendor/gix-transport/src/client/blocking_io/connect.rs
@@ -0,0 +1,68 @@
+pub use crate::client::non_io_types::connect::{Error, Options};
+
+pub(crate) mod function {
+ use std::convert::TryInto;
+
+ use crate::client::{non_io_types::connect::Error, Transport};
+
+ /// A general purpose connector connecting to a repository identified by the given `url`.
+ ///
+ /// This includes connections to
+ /// [local repositories][crate::client::file::connect()],
+ /// [repositories over ssh][crate::client::ssh::connect()],
+ /// [git daemons][crate::client::git::connect()],
+ /// and if compiled in connections to [git repositories over https][crate::client::http::connect()].
+ ///
+ /// Use `options` to further control specifics of the transport resulting from the connection.
+ pub fn connect<Url, E>(url: Url, options: super::Options) -> Result<Box<dyn Transport + Send>, Error>
+ where
+ Url: TryInto<gix_url::Url, Error = E>,
+ gix_url::parse::Error: From<E>,
+ {
+ let mut url = url.try_into().map_err(gix_url::parse::Error::from)?;
+ Ok(match url.scheme {
+ gix_url::Scheme::Ext(_) => return Err(Error::UnsupportedScheme(url.scheme)),
+ gix_url::Scheme::File => {
+ if url.user().is_some() || url.host().is_some() || url.port.is_some() {
+ return Err(Error::UnsupportedUrlTokens {
+ url: url.to_bstring(),
+ scheme: url.scheme,
+ });
+ }
+ Box::new(
+ crate::client::blocking_io::file::connect(url.path, options.version)
+ .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?,
+ )
+ }
+ gix_url::Scheme::Ssh => Box::new({
+ crate::client::blocking_io::ssh::connect(url, options.version, options.ssh)
+ .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?
+ }),
+ gix_url::Scheme::Git => {
+ if url.user().is_some() {
+ return Err(Error::UnsupportedUrlTokens {
+ url: url.to_bstring(),
+ scheme: url.scheme,
+ });
+ }
+ Box::new({
+ let path = std::mem::take(&mut url.path);
+ crate::client::git::connect(
+ url.host().expect("host is present in url"),
+ path,
+ options.version,
+ url.port,
+ )
+ .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?
+ })
+ }
+ #[cfg(not(any(feature = "http-client-curl", feature = "http-client-reqwest")))]
+ gix_url::Scheme::Https | gix_url::Scheme::Http => return Err(Error::CompiledWithoutHttp(url.scheme)),
+ #[cfg(any(feature = "http-client-curl", feature = "http-client-reqwest"))]
+ gix_url::Scheme::Https | gix_url::Scheme::Http => Box::new(crate::client::http::connect(
+ &url.to_bstring().to_string(),
+ options.version,
+ )),
+ })
+ }
+}
diff --git a/vendor/gix-transport/src/client/blocking_io/file.rs b/vendor/gix-transport/src/client/blocking_io/file.rs
new file mode 100644
index 000000000..d80fe55df
--- /dev/null
+++ b/vendor/gix-transport/src/client/blocking_io/file.rs
@@ -0,0 +1,288 @@
+use std::{
+ any::Any,
+ borrow::Cow,
+ error::Error,
+ ffi::{OsStr, OsString},
+ io::Write,
+ process::{self, Stdio},
+};
+
+use bstr::{io::BufReadExt, BStr, BString, ByteSlice};
+
+use crate::{
+ client::{self, git, ssh, MessageKind, RequestWriter, SetServiceResponse, WriteMode},
+ Protocol, Service,
+};
+
+// from https://github.com/git/git/blob/20de7e7e4f4e9ae52e6cc7cfaa6469f186ddb0fa/environment.c#L115:L115
+const ENV_VARS_TO_REMOVE: &[&str] = &[
+ "GIT_ALTERNATE_OBJECT_DIRECTORIES",
+ "GIT_CONFIG",
+ "GIT_CONFIG_PARAMETERS",
+ "GIT_OBJECT_DIRECTORY",
+ "GIT_DIR",
+ "GIT_WORK_TREE",
+ "GIT_IMPLICIT_WORK_TREE",
+ "GIT_GRAFT_FILE",
+ "GIT_INDEX_FILE",
+ "GIT_NO_REPLACE_OBJECTS",
+ "GIT_REPLACE_REF_BASE",
+ "GIT_PREFIX",
+ "GIT_INTERNAL_SUPER_PREFIX",
+ "GIT_SHALLOW_FILE",
+ "GIT_COMMON_DIR",
+ "GIT_CONFIG_COUNT",
+];
+
+/// A utility to spawn a helper process to actually transmit data, possibly over `ssh`.
+///
+/// It can only be instantiated using the local [`connect()`] or [ssh connect][crate::client::ssh::connect()].
+pub struct SpawnProcessOnDemand {
+ desired_version: Protocol,
+ url: gix_url::Url,
+ path: BString,
+ ssh_cmd: Option<(OsString, ssh::ProgramKind)>,
+ /// The environment variables to set in the invoked command.
+ envs: Vec<(&'static str, String)>,
+ ssh_disallow_shell: bool,
+ connection: Option<git::Connection<Box<dyn std::io::Read + Send>, process::ChildStdin>>,
+ child: Option<process::Child>,
+}
+
+impl SpawnProcessOnDemand {
+ pub(crate) fn new_ssh(
+ url: gix_url::Url,
+ program: impl Into<OsString>,
+ path: BString,
+ ssh_kind: ssh::ProgramKind,
+ ssh_disallow_shell: bool,
+ version: Protocol,
+ ) -> SpawnProcessOnDemand {
+ SpawnProcessOnDemand {
+ url,
+ path,
+ ssh_cmd: Some((program.into(), ssh_kind)),
+ envs: Default::default(),
+ ssh_disallow_shell,
+ child: None,
+ connection: None,
+ desired_version: version,
+ }
+ }
+ fn new_local(path: BString, version: Protocol) -> SpawnProcessOnDemand {
+ SpawnProcessOnDemand {
+ url: gix_url::Url::from_parts_as_alternative_form(gix_url::Scheme::File, None, None, None, path.clone())
+ .expect("valid url"),
+ path,
+ ssh_cmd: None,
+ envs: (version != Protocol::V1)
+ .then(|| vec![("GIT_PROTOCOL", format!("version={}", version as usize))])
+ .unwrap_or_default(),
+ ssh_disallow_shell: false,
+ child: None,
+ connection: None,
+ desired_version: version,
+ }
+ }
+}
+
+impl client::TransportWithoutIO for SpawnProcessOnDemand {
+ fn set_identity(&mut self, identity: gix_sec::identity::Account) -> Result<(), client::Error> {
+ if self.url.scheme == gix_url::Scheme::Ssh {
+ self.url
+ .set_user((!identity.username.is_empty()).then_some(identity.username));
+ Ok(())
+ } else {
+ Err(client::Error::AuthenticationUnsupported)
+ }
+ }
+
+ fn request(
+ &mut self,
+ write_mode: WriteMode,
+ on_into_read: MessageKind,
+ ) -> Result<RequestWriter<'_>, client::Error> {
+ self.connection
+ .as_mut()
+ .expect("handshake() to have been called first")
+ .request(write_mode, on_into_read)
+ }
+
+ fn to_url(&self) -> Cow<'_, BStr> {
+ Cow::Owned(self.url.to_bstring())
+ }
+
+ fn connection_persists_across_multiple_requests(&self) -> bool {
+ true
+ }
+
+ fn configure(&mut self, _config: &dyn Any) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
+ Ok(())
+ }
+}
+
+struct ReadStdoutFailOnError {
+ recv: std::sync::mpsc::Receiver<std::io::Error>,
+ read: std::process::ChildStdout,
+}
+
+fn supervise_stderr(
+ ssh_kind: ssh::ProgramKind,
+ stderr: std::process::ChildStderr,
+ stdout: std::process::ChildStdout,
+) -> ReadStdoutFailOnError {
+ impl ReadStdoutFailOnError {
+ fn swap_err_if_present_in_stderr(&self, wanted: usize, res: std::io::Result<usize>) -> std::io::Result<usize> {
+ match self.recv.try_recv().ok() {
+ Some(err) => Err(err),
+ None => match res {
+ Ok(n) if n == wanted => Ok(n),
+ Ok(n) => {
+ // TODO: fix this
+ // When parsing refs this seems to happen legitimately
+ // (even though we read packet lines only and should always know exactly how much to read)
+ // Maybe this still happens in `read_exact()` as sometimes we just don't get enough bytes
+ // despite knowing how many.
+ // To prevent deadlock, we have to set a timeout which slows down legitimate parts of the protocol.
+ // This code was specifically written to make the `cargo` test-suite pass, and we can reduce
+ // the timeouts even more once there is a native ssh transport that is used by `cargo`, it will
+ // be able to handle these properly.
+ // Alternatively, one could implement something like `read2` to avoid blocking on stderr entirely.
+ self.recv
+ .recv_timeout(std::time::Duration::from_millis(5))
+ .ok()
+ .map(Err)
+ .unwrap_or(Ok(n))
+ }
+ Err(err) => Err(self.recv.recv().ok().unwrap_or(err)),
+ },
+ }
+ }
+ }
+ impl std::io::Read for ReadStdoutFailOnError {
+ fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+ let res = self.read.read(buf);
+ self.swap_err_if_present_in_stderr(buf.len(), res)
+ }
+ }
+
+ let (send, recv) = std::sync::mpsc::sync_channel(1);
+ std::thread::Builder::new()
+ .name("supervise ssh stderr".into())
+ .stack_size(128 * 1024)
+ .spawn(move || -> std::io::Result<()> {
+ let mut process_stderr = std::io::stderr();
+ for line in std::io::BufReader::new(stderr).byte_lines() {
+ let line = line?;
+ match ssh_kind.line_to_err(line.into()) {
+ Ok(err) => {
+ send.send(err).ok();
+ }
+ Err(line) => {
+ process_stderr.write_all(&line).ok();
+ writeln!(&process_stderr).ok();
+ }
+ }
+ }
+ Ok(())
+ })
+ .expect("named threads with small stack work on all platforms");
+ ReadStdoutFailOnError { read: stdout, recv }
+}
+
+impl client::Transport for SpawnProcessOnDemand {
+ fn handshake<'a>(
+ &mut self,
+ service: Service,
+ extra_parameters: &'a [(&'a str, Option<&'a str>)],
+ ) -> Result<SetServiceResponse<'_>, client::Error> {
+ let (mut cmd, ssh_kind, cmd_name) = match &self.ssh_cmd {
+ Some((command, kind)) => (
+ kind.prepare_invocation(command, &self.url, self.desired_version, self.ssh_disallow_shell)
+ .map_err(client::Error::SshInvocation)?
+ .stderr(Stdio::piped()),
+ Some(*kind),
+ Cow::Owned(command.to_owned()),
+ ),
+ None => (
+ gix_command::prepare(service.as_str()).stderr(Stdio::null()),
+ None,
+ Cow::Borrowed(OsStr::new(service.as_str())),
+ ),
+ };
+ cmd.stdin = Stdio::piped();
+ cmd.stdout = Stdio::piped();
+ let repo_path = if self.ssh_cmd.is_some() {
+ cmd.args.push(service.as_str().into());
+ gix_quote::single(self.path.as_ref()).to_os_str_lossy().into_owned()
+ } else {
+ self.path.to_os_str_lossy().into_owned()
+ };
+ cmd.args.push(repo_path);
+
+ let mut cmd = std::process::Command::from(cmd);
+ for env_to_remove in ENV_VARS_TO_REMOVE {
+ cmd.env_remove(env_to_remove);
+ }
+ cmd.envs(std::mem::take(&mut self.envs));
+
+ let mut child = cmd.spawn().map_err(|err| client::Error::InvokeProgram {
+ source: err,
+ command: cmd_name.into_owned(),
+ })?;
+ let stdout: Box<dyn std::io::Read + Send> = match ssh_kind {
+ Some(ssh_kind) => Box::new(supervise_stderr(
+ ssh_kind,
+ child.stderr.take().expect("configured beforehand"),
+ child.stdout.take().expect("configured"),
+ )),
+ None => Box::new(child.stdout.take().expect("stdout configured")),
+ };
+ self.connection = Some(git::Connection::new_for_spawned_process(
+ stdout,
+ child.stdin.take().expect("stdin configured"),
+ self.desired_version,
+ self.path.clone(),
+ ));
+ self.child = Some(child);
+ self.connection
+ .as_mut()
+ .expect("connection to be there right after setting it")
+ .handshake(service, extra_parameters)
+ }
+}
+
+/// Connect to a locally readable repository at `path` using the given `desired_version`.
+///
+/// This will spawn a `git` process locally.
+pub fn connect(
+ path: impl Into<BString>,
+ desired_version: Protocol,
+) -> Result<SpawnProcessOnDemand, std::convert::Infallible> {
+ Ok(SpawnProcessOnDemand::new_local(path.into(), desired_version))
+}
+
+#[cfg(test)]
+mod tests {
+ mod ssh {
+ mod connect {
+ use crate::{client::blocking_io::ssh::connect, Protocol};
+
+ #[test]
+ fn path() {
+ for (url, expected) in [
+ ("ssh://host.xy/~/repo", "~/repo"),
+ ("ssh://host.xy/~username/repo", "~username/repo"),
+ ("user@host.xy:/username/repo", "/username/repo"),
+ ("user@host.xy:username/repo", "username/repo"),
+ ("user@host.xy:../username/repo", "../username/repo"),
+ ("user@host.xy:~/repo", "~/repo"),
+ ] {
+ let url = gix_url::parse((*url).into()).expect("valid url");
+ let cmd = connect(url, Protocol::V1, Default::default()).expect("parse success");
+ assert_eq!(cmd.path, expected, "the path will be substituted by the remote shell");
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/gix-transport/src/client/blocking_io/http/curl/mod.rs b/vendor/gix-transport/src/client/blocking_io/http/curl/mod.rs
new file mode 100644
index 000000000..ad980dd20
--- /dev/null
+++ b/vendor/gix-transport/src/client/blocking_io/http/curl/mod.rs
@@ -0,0 +1,169 @@
+use std::{
+ sync::mpsc::{Receiver, SyncSender},
+ thread,
+};
+
+use gix_features::io;
+
+use crate::client::{blocking_io::http, http::traits::PostBodyDataKind};
+
+mod remote;
+
+/// Options to configure the `curl` HTTP handler.
+#[derive(Default)]
+pub struct Options {
+ /// If `true` and runtime configuration is possible for `curl` backends, certificates revocation will be checked.
+ ///
+ /// This only works on windows apparently. Ignored if `None`.
+ pub schannel_check_revoke: Option<bool>,
+}
+
+/// The error returned by the 'remote' helper, a purely internal construct to perform http requests.
+///
+/// It can be used for downcasting errors, which are boxed to hide the actual implementation.
+#[derive(Debug, thiserror::Error)]
+#[allow(missing_docs)]
+pub enum Error {
+ #[error(transparent)]
+ Curl(#[from] curl::Error),
+ #[error(transparent)]
+ Redirect(#[from] http::redirect::Error),
+ #[error("Could not finish reading all data to post to the remote")]
+ ReadPostBody(#[from] std::io::Error),
+ #[error(transparent)]
+ Authenticate(#[from] gix_credentials::protocol::Error),
+}
+
+impl crate::IsSpuriousError for Error {
+ fn is_spurious(&self) -> bool {
+ match self {
+ Error::Curl(err) => curl_is_spurious(err),
+ _ => false,
+ }
+ }
+}
+
+pub(crate) fn curl_is_spurious(err: &curl::Error) -> bool {
+ err.is_couldnt_connect()
+ || err.is_couldnt_resolve_proxy()
+ || err.is_couldnt_resolve_host()
+ || err.is_operation_timedout()
+ || err.is_recv_error()
+ || err.is_send_error()
+ || err.is_http2_error()
+ || err.is_http2_stream_error()
+ || err.is_ssl_connect_error()
+ || err.is_partial_file()
+}
+
+/// A utility to abstract interactions with curl handles.
+pub struct Curl {
+ req: SyncSender<remote::Request>,
+ res: Receiver<remote::Response>,
+ handle: Option<thread::JoinHandle<Result<(), Error>>>,
+ config: http::Options,
+}
+
+impl Curl {
+ fn restore_thread_after_failure(&mut self) -> http::Error {
+ let err_that_brought_thread_down = self
+ .handle
+ .take()
+ .expect("thread handle present")
+ .join()
+ .expect("handler thread should never panic")
+ .expect_err("something should have gone wrong with curl (we join on error only)");
+ let (handle, req, res) = remote::new();
+ self.handle = Some(handle);
+ self.req = req;
+ self.res = res;
+ err_that_brought_thread_down.into()
+ }
+
+ fn make_request(
+ &mut self,
+ url: &str,
+ base_url: &str,
+ headers: impl IntoIterator<Item = impl AsRef<str>>,
+ upload_body_kind: Option<PostBodyDataKind>,
+ ) -> Result<http::PostResponse<io::pipe::Reader, io::pipe::Reader, io::pipe::Writer>, http::Error> {
+ let mut list = curl::easy::List::new();
+ for header in headers {
+ list.append(header.as_ref())?;
+ }
+ if self
+ .req
+ .send(remote::Request {
+ url: url.to_owned(),
+ base_url: base_url.to_owned(),
+ headers: list,
+ upload_body_kind,
+ config: self.config.clone(),
+ })
+ .is_err()
+ {
+ return Err(self.restore_thread_after_failure());
+ }
+ let remote::Response {
+ headers,
+ body,
+ upload_body,
+ } = match self.res.recv() {
+ Ok(res) => res,
+ Err(_) => return Err(self.restore_thread_after_failure()),
+ };
+ Ok(http::PostResponse {
+ post_body: upload_body,
+ headers,
+ body,
+ })
+ }
+}
+
+impl Default for Curl {
+ fn default() -> Self {
+ let (handle, req, res) = remote::new();
+ Curl {
+ handle: Some(handle),
+ req,
+ res,
+ config: http::Options::default(),
+ }
+ }
+}
+
+#[allow(clippy::type_complexity)]
+impl http::Http for Curl {
+ type Headers = io::pipe::Reader;
+ type ResponseBody = io::pipe::Reader;
+ type PostBody = io::pipe::Writer;
+
+ fn get(
+ &mut self,
+ url: &str,
+ base_url: &str,
+ headers: impl IntoIterator<Item = impl AsRef<str>>,
+ ) -> Result<http::GetResponse<Self::Headers, Self::ResponseBody>, http::Error> {
+ self.make_request(url, base_url, headers, None).map(Into::into)
+ }
+
+ fn post(
+ &mut self,
+ url: &str,
+ base_url: &str,
+ headers: impl IntoIterator<Item = impl AsRef<str>>,
+ body: PostBodyDataKind,
+ ) -> Result<http::PostResponse<Self::Headers, Self::ResponseBody, Self::PostBody>, http::Error> {
+ self.make_request(url, base_url, headers, Some(body))
+ }
+
+ fn configure(
+ &mut self,
+ config: &dyn std::any::Any,
+ ) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
+ if let Some(config) = config.downcast_ref::<http::Options>() {
+ self.config = config.clone();
+ }
+ Ok(())
+ }
+}
diff --git a/vendor/gix-transport/src/client/blocking_io/http/curl/remote.rs b/vendor/gix-transport/src/client/blocking_io/http/curl/remote.rs
new file mode 100644
index 000000000..8f5867698
--- /dev/null
+++ b/vendor/gix-transport/src/client/blocking_io/http/curl/remote.rs
@@ -0,0 +1,392 @@
+use std::{
+ io,
+ io::{Read, Write},
+ sync::mpsc::{sync_channel, Receiver, SyncSender, TrySendError},
+ thread,
+ time::Duration,
+};
+
+use curl::easy::{Auth, Easy2};
+use gix_features::io::pipe;
+
+use crate::client::{
+ blocking_io::http::{self, curl::Error, redirect},
+ http::{
+ curl::curl_is_spurious,
+ options::{FollowRedirects, HttpVersion, ProxyAuthMethod, SslVersion},
+ traits::PostBodyDataKind,
+ },
+};
+
+enum StreamOrBuffer {
+ Stream(pipe::Reader),
+ Buffer(std::io::Cursor<Vec<u8>>),
+}
+
+#[derive(Default)]
+struct Handler {
+ send_header: Option<pipe::Writer>,
+ send_data: Option<pipe::Writer>,
+ receive_body: Option<StreamOrBuffer>,
+ checked_status: bool,
+ last_status: usize,
+ follow: FollowRedirects,
+}
+
+impl Handler {
+ fn reset(&mut self) {
+ self.checked_status = false;
+ self.last_status = 0;
+ self.follow = FollowRedirects::default();
+ }
+ fn parse_status_inner(data: &[u8]) -> Result<usize, Box<dyn std::error::Error + Send + Sync>> {
+ let code = data
+ .split(|b| *b == b' ')
+ .nth(1)
+ .ok_or("Expected HTTP/<VERSION> STATUS")?;
+ let code = std::str::from_utf8(code)?;
+ code.parse().map_err(Into::into)
+ }
+ fn parse_status(data: &[u8], follow: FollowRedirects) -> Option<(usize, Box<dyn std::error::Error + Send + Sync>)> {
+ let valid_end = match follow {
+ FollowRedirects::Initial | FollowRedirects::All => 308,
+ FollowRedirects::None => 299,
+ };
+ match Self::parse_status_inner(data) {
+ Ok(status) if !(200..=valid_end).contains(&status) => {
+ Some((status, format!("Received HTTP status {status}").into()))
+ }
+ Ok(_) => None,
+ Err(err) => Some((500, err)),
+ }
+ }
+}
+
+impl curl::easy::Handler for Handler {
+ fn write(&mut self, data: &[u8]) -> Result<usize, curl::easy::WriteError> {
+ drop(self.send_header.take()); // signal header readers to stop trying
+ match self.send_data.as_mut() {
+ Some(writer) => writer.write_all(data).map(|_| data.len()).or(Ok(0)),
+ None => Ok(0), // nothing more to receive, reader is done
+ }
+ }
+ fn read(&mut self, data: &mut [u8]) -> Result<usize, curl::easy::ReadError> {
+ match self.receive_body.as_mut() {
+ Some(StreamOrBuffer::Stream(reader)) => reader.read(data).map_err(|_err| curl::easy::ReadError::Abort),
+ Some(StreamOrBuffer::Buffer(cursor)) => cursor.read(data).map_err(|_err| curl::easy::ReadError::Abort),
+ None => Ok(0), // nothing more to read/writer depleted
+ }
+ }
+
+ fn header(&mut self, data: &[u8]) -> bool {
+ if let Some(writer) = self.send_header.as_mut() {
+ if self.checked_status {
+ writer.write_all(data).ok();
+ } else {
+ self.checked_status = true;
+ self.last_status = 200;
+ if let Some((status, err)) = Handler::parse_status(data, self.follow) {
+ self.last_status = status;
+ writer
+ .channel
+ .send(Err(io::Error::new(
+ if status == 401 {
+ io::ErrorKind::PermissionDenied
+ } else if (500..600).contains(&status) {
+ io::ErrorKind::ConnectionAborted
+ } else {
+ io::ErrorKind::Other
+ },
+ err,
+ )))
+ .ok();
+ }
+ }
+ };
+ true
+ }
+}
+
+pub struct Request {
+ pub url: String,
+ pub base_url: String,
+ pub headers: curl::easy::List,
+ pub upload_body_kind: Option<PostBodyDataKind>,
+ pub config: http::Options,
+}
+
+pub struct Response {
+ pub headers: pipe::Reader,
+ pub body: pipe::Reader,
+ pub upload_body: pipe::Writer,
+}
+
+pub fn new() -> (
+ thread::JoinHandle<Result<(), Error>>,
+ SyncSender<Request>,
+ Receiver<Response>,
+) {
+ let (req_send, req_recv) = sync_channel(0);
+ let (res_send, res_recv) = sync_channel(0);
+ let handle = std::thread::spawn(move || -> Result<(), Error> {
+ let mut handle = Easy2::new(Handler::default());
+ // We don't wait for the possibility for pipelining to become clear, and curl tries to reuse connections by default anyway.
+ handle.pipewait(false)?;
+ handle.tcp_keepalive(true)?;
+
+ let mut follow = None;
+ let mut redirected_base_url = None::<String>;
+
+ for Request {
+ url,
+ base_url,
+ mut headers,
+ upload_body_kind,
+ config:
+ http::Options {
+ extra_headers,
+ follow_redirects,
+ low_speed_limit_bytes_per_second,
+ low_speed_time_seconds,
+ connect_timeout,
+ proxy,
+ no_proxy,
+ proxy_auth_method,
+ user_agent,
+ proxy_authenticate,
+ verbose,
+ ssl_ca_info,
+ ssl_version,
+ http_version,
+ backend,
+ },
+ } in req_recv
+ {
+ let effective_url = redirect::swap_tails(redirected_base_url.as_deref(), &base_url, url.clone());
+ handle.url(&effective_url)?;
+
+ handle.post(upload_body_kind.is_some())?;
+ for header in extra_headers {
+ headers.append(&header)?;
+ }
+ // needed to avoid sending Expect: 100-continue, which adds another response and only CURL wants that
+ headers.append("Expect:")?;
+ handle.verbose(verbose)?;
+
+ if let Some(ca_info) = ssl_ca_info {
+ handle.cainfo(ca_info)?;
+ }
+
+ if let Some(ref mut curl_options) = backend.as_ref().and_then(|backend| backend.lock().ok()) {
+ if let Some(opts) = curl_options.downcast_mut::<super::Options>() {
+ if let Some(enabled) = opts.schannel_check_revoke {
+ handle.ssl_options(curl::easy::SslOpt::new().no_revoke(!enabled))?;
+ }
+ }
+ }
+
+ if let Some(ssl_version) = ssl_version {
+ let (min, max) = ssl_version.min_max();
+ if min == max {
+ handle.ssl_version(to_curl_ssl_version(min))?;
+ } else {
+ handle.ssl_min_max_version(to_curl_ssl_version(min), to_curl_ssl_version(max))?;
+ }
+ }
+
+ if let Some(http_version) = http_version {
+ let version = match http_version {
+ HttpVersion::V1_1 => curl::easy::HttpVersion::V11,
+ HttpVersion::V2 => curl::easy::HttpVersion::V2,
+ };
+ // Failing to set the version isn't critical, and may indeed fail depending on the version
+ // of libcurl we are built against.
+ // Furthermore, `git` itself doesn't actually check for errors when configuring curl at all,
+ // treating all or most flags as non-critical.
+ handle.http_version(version).ok();
+ }
+
+ let mut proxy_auth_action = None;
+ if let Some(proxy) = proxy {
+ handle.proxy(&proxy)?;
+ let proxy_type = if proxy.starts_with("socks5h") {
+ curl::easy::ProxyType::Socks5Hostname
+ } else if proxy.starts_with("socks5") {
+ curl::easy::ProxyType::Socks5
+ } else if proxy.starts_with("socks4a") {
+ curl::easy::ProxyType::Socks4a
+ } else if proxy.starts_with("socks") {
+ curl::easy::ProxyType::Socks4
+ } else {
+ curl::easy::ProxyType::Http
+ };
+ handle.proxy_type(proxy_type)?;
+
+ if let Some((obtain_creds_action, authenticate)) = proxy_authenticate {
+ let creds = authenticate.lock().expect("no panics in other threads")(obtain_creds_action)?
+ .expect("action to fetch credentials");
+ handle.proxy_username(&creds.identity.username)?;
+ handle.proxy_password(&creds.identity.password)?;
+ proxy_auth_action = Some((creds.next, authenticate));
+ }
+ }
+ if let Some(no_proxy) = no_proxy {
+ handle.noproxy(&no_proxy)?;
+ }
+ if let Some(user_agent) = user_agent {
+ handle.useragent(&user_agent)?;
+ }
+ handle.transfer_encoding(false)?;
+ if let Some(timeout) = connect_timeout {
+ handle.connect_timeout(timeout)?;
+ }
+ {
+ let mut auth = Auth::new();
+ match proxy_auth_method {
+ ProxyAuthMethod::AnyAuth => auth
+ .basic(true)
+ .digest(true)
+ .digest_ie(true)
+ .gssnegotiate(true)
+ .ntlm(true)
+ .aws_sigv4(true),
+ ProxyAuthMethod::Basic => auth.basic(true),
+ ProxyAuthMethod::Digest => auth.digest(true),
+ ProxyAuthMethod::Negotiate => auth.digest_ie(true),
+ ProxyAuthMethod::Ntlm => auth.ntlm(true),
+ };
+ handle.proxy_auth(&auth)?;
+ }
+ handle.tcp_keepalive(true)?;
+
+ if low_speed_time_seconds > 0 && low_speed_limit_bytes_per_second > 0 {
+ handle.low_speed_limit(low_speed_limit_bytes_per_second)?;
+ handle.low_speed_time(Duration::from_secs(low_speed_time_seconds))?;
+ }
+ let (receive_data, receive_headers, send_body, mut receive_body) = {
+ let handler = handle.get_mut();
+ let (send, receive_data) = pipe::unidirectional(1);
+ handler.send_data = Some(send);
+ let (send, receive_headers) = pipe::unidirectional(1);
+ handler.send_header = Some(send);
+ let (send_body, receive_body) = pipe::unidirectional(None);
+ (receive_data, receive_headers, send_body, receive_body)
+ };
+
+ let follow = follow.get_or_insert(follow_redirects);
+ handle.get_mut().follow = *follow;
+ handle.follow_location(matches!(*follow, FollowRedirects::Initial | FollowRedirects::All))?;
+
+ if *follow == FollowRedirects::Initial {
+ *follow = FollowRedirects::None;
+ }
+
+ if res_send
+ .send(Response {
+ headers: receive_headers,
+ body: receive_data,
+ upload_body: send_body,
+ })
+ .is_err()
+ {
+ break;
+ }
+
+ handle.get_mut().receive_body = Some(match upload_body_kind {
+ Some(PostBodyDataKind::Unbounded) | None => StreamOrBuffer::Stream(receive_body),
+ Some(PostBodyDataKind::BoundedAndFitsIntoMemory) => {
+ let mut buf = Vec::<u8>::with_capacity(512);
+ receive_body.read_to_end(&mut buf)?;
+ handle.post_field_size(buf.len() as u64)?;
+ drop(receive_body);
+ StreamOrBuffer::Buffer(std::io::Cursor::new(buf))
+ }
+ });
+ handle.http_headers(headers)?;
+
+ if let Err(err) = handle.perform() {
+ let handler = handle.get_mut();
+ handler.reset();
+
+ if let Some((action, authenticate)) = proxy_auth_action {
+ authenticate.lock().expect("no panics in other threads")(action.erase()).ok();
+ }
+ let err = Err(io::Error::new(
+ if curl_is_spurious(&err) {
+ std::io::ErrorKind::ConnectionReset
+ } else {
+ std::io::ErrorKind::Other
+ },
+ err,
+ ));
+ handler.receive_body.take();
+ match (handler.send_header.take(), handler.send_data.take()) {
+ (Some(header), mut data) => {
+ if let Err(TrySendError::Disconnected(err)) | Err(TrySendError::Full(err)) =
+ header.channel.try_send(err)
+ {
+ if let Some(body) = data.take() {
+ body.channel.try_send(err).ok();
+ }
+ }
+ }
+ (None, Some(body)) => {
+ body.channel.try_send(err).ok();
+ }
+ (None, None) => {}
+ };
+ } else {
+ let handler = handle.get_mut();
+ if let Some((action, authenticate)) = proxy_auth_action {
+ authenticate.lock().expect("no panics in other threads")(if handler.last_status == 200 {
+ action.store()
+ } else {
+ action.erase()
+ })?;
+ }
+ handler.reset();
+ handler.receive_body.take();
+ handler.send_header.take();
+ handler.send_data.take();
+ let actual_url = handle
+ .effective_url()?
+ .expect("effective url is present and valid UTF-8");
+ if actual_url != effective_url {
+ redirected_base_url = redirect::base_url(actual_url, &base_url, url)?.into();
+ }
+ }
+ }
+ Ok(())
+ });
+ (handle, req_send, res_recv)
+}
+
+fn to_curl_ssl_version(vers: SslVersion) -> curl::easy::SslVersion {
+ use curl::easy::SslVersion::*;
+ match vers {
+ SslVersion::Default => Default,
+ SslVersion::TlsV1 => Tlsv1,
+ SslVersion::SslV2 => Sslv2,
+ SslVersion::SslV3 => Sslv3,
+ SslVersion::TlsV1_0 => Tlsv10,
+ SslVersion::TlsV1_1 => Tlsv11,
+ SslVersion::TlsV1_2 => Tlsv12,
+ SslVersion::TlsV1_3 => Tlsv13,
+ }
+}
+
+impl From<Error> for http::Error {
+ fn from(err: Error) -> Self {
+ http::Error::Detail {
+ description: err.to_string(),
+ }
+ }
+}
+
+impl From<curl::Error> for http::Error {
+ fn from(err: curl::Error) -> Self {
+ http::Error::Detail {
+ description: err.to_string(),
+ }
+ }
+}
diff --git a/vendor/gix-transport/src/client/blocking_io/http/mod.rs b/vendor/gix-transport/src/client/blocking_io/http/mod.rs
new file mode 100644
index 000000000..d88d6bf26
--- /dev/null
+++ b/vendor/gix-transport/src/client/blocking_io/http/mod.rs
@@ -0,0 +1,512 @@
+use std::{
+ any::Any,
+ borrow::Cow,
+ io::{BufRead, Read},
+ path::PathBuf,
+ sync::{Arc, Mutex},
+};
+
+use base64::Engine;
+use bstr::BStr;
+use gix_packetline::PacketLineRef;
+pub use traits::{Error, GetResponse, Http, PostBodyDataKind, PostResponse};
+
+use crate::{
+ client::{
+ self,
+ blocking_io::bufread_ext::ReadlineBufRead,
+ capabilities,
+ http::options::{HttpVersion, SslVersionRangeInclusive},
+ Capabilities, ExtendedBufRead, HandleProgress, MessageKind, RequestWriter,
+ },
+ Protocol, Service,
+};
+
+#[cfg(all(feature = "http-client-reqwest", feature = "http-client-curl"))]
+compile_error!("Cannot set both 'http-client-reqwest' and 'http-client-curl' features as they are mutually exclusive");
+
+#[cfg(feature = "http-client-curl")]
+///
+pub mod curl;
+
+/// The experimental `reqwest` backend.
+///
+/// It doesn't support any of the shared http options yet, but can be seen as example on how to integrate blocking `http` backends.
+/// There is also nothing that would prevent it from becoming a fully-featured HTTP backend except for demand and time.
+#[cfg(feature = "http-client-reqwest")]
+pub mod reqwest;
+
+mod traits;
+
+///
+pub mod options {
+ /// A function to authenticate a URL.
+ pub type AuthenticateFn =
+ dyn FnMut(gix_credentials::helper::Action) -> gix_credentials::protocol::Result + Send + Sync;
+
+ /// Possible settings for the `http.followRedirects` configuration option.
+ #[derive(Debug, Copy, Clone, PartialEq, Eq)]
+ pub enum FollowRedirects {
+ /// Follow only the first redirect request, most suitable for typical git requests.
+ Initial,
+ /// Follow all redirect requests from the server unconditionally
+ All,
+ /// Follow no redirect request.
+ None,
+ }
+
+ impl Default for FollowRedirects {
+ fn default() -> Self {
+ FollowRedirects::Initial
+ }
+ }
+
+ /// The way to configure a proxy for authentication if a username is present in the configured proxy.
+ #[derive(Debug, Copy, Clone, PartialEq, Eq)]
+ pub enum ProxyAuthMethod {
+ /// Automatically pick a suitable authentication method.
+ AnyAuth,
+ ///HTTP basic authentication.
+ Basic,
+ /// Http digest authentication to prevent a password to be passed in clear text.
+ Digest,
+ /// GSS negotiate authentication.
+ Negotiate,
+ /// NTLM authentication
+ Ntlm,
+ }
+
+ impl Default for ProxyAuthMethod {
+ fn default() -> Self {
+ ProxyAuthMethod::AnyAuth
+ }
+ }
+
+ /// Available SSL version numbers.
+ #[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
+ #[allow(missing_docs)]
+ pub enum SslVersion {
+ /// The implementation default, which is unknown to this layer of abstraction.
+ Default,
+ TlsV1,
+ SslV2,
+ SslV3,
+ TlsV1_0,
+ TlsV1_1,
+ TlsV1_2,
+ TlsV1_3,
+ }
+
+ /// Available HTTP version numbers.
+ #[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
+ #[allow(missing_docs)]
+ pub enum HttpVersion {
+ /// Equivalent to HTTP/1.1
+ V1_1,
+ /// Equivalent to HTTP/2
+ V2,
+ }
+
+ /// The desired range of acceptable SSL versions, or the single version to allow if both are set to the same value.
+ #[derive(Debug, Copy, Clone, PartialEq, Eq)]
+ pub struct SslVersionRangeInclusive {
+ /// The smallest allowed ssl version to use.
+ pub min: SslVersion,
+ /// The highest allowed ssl version to use.
+ pub max: SslVersion,
+ }
+
+ impl SslVersionRangeInclusive {
+ /// Return `min` and `max` fields in the right order so `min` is smaller or equal to `max`.
+ pub fn min_max(&self) -> (SslVersion, SslVersion) {
+ if self.min > self.max {
+ (self.max, self.min)
+ } else {
+ (self.min, self.max)
+ }
+ }
+ }
+}
+
+/// Options to configure http requests.
+// TODO: testing most of these fields requires a lot of effort, unless special flags to introspect ongoing requests are added.
+#[derive(Default, Clone)]
+pub struct Options {
+ /// Headers to be added to every request.
+ /// They are applied unconditionally and are expected to be valid as they occour in an HTTP request, like `header: value`, without newlines.
+ ///
+ /// Refers to `http.extraHeader` multi-var.
+ pub extra_headers: Vec<String>,
+ /// How to handle redirects.
+ ///
+ /// Refers to `http.followRedirects`.
+ pub follow_redirects: options::FollowRedirects,
+ /// Used in conjunction with `low_speed_time_seconds`, any non-0 value signals the amount of bytes per second at least to avoid
+ /// aborting the connection.
+ ///
+ /// Refers to `http.lowSpeedLimit`.
+ pub low_speed_limit_bytes_per_second: u32,
+ /// Used in conjunction with `low_speed_bytes_per_second`, any non-0 value signals the amount seconds the minimal amount
+ /// of bytes per second isn't reached.
+ ///
+ /// Refers to `http.lowSpeedTime`.
+ pub low_speed_time_seconds: u64,
+ /// A curl-style proxy declaration of the form `[protocol://][user[:password]@]proxyhost[:port]`.
+ ///
+ /// Note that an empty string means the proxy is disabled entirely.
+ /// Refers to `http.proxy`.
+ pub proxy: Option<String>,
+ /// The comma-separated list of hosts to not send through the `proxy`, or `*` to entirely disable all proxying.
+ pub no_proxy: Option<String>,
+ /// The way to authenticate against the proxy if the `proxy` field contains a username.
+ ///
+ /// Refers to `http.proxyAuthMethod`.
+ pub proxy_auth_method: options::ProxyAuthMethod,
+ /// If authentication is needed for the proxy as its URL contains a username, this method must be set to provide a password
+ /// for it before making the request, and to store it if the connection succeeds.
+ pub proxy_authenticate: Option<(
+ gix_credentials::helper::Action,
+ Arc<std::sync::Mutex<options::AuthenticateFn>>,
+ )>,
+ /// The `HTTP` `USER_AGENT` string presented to an `HTTP` server, notably not the user agent present to the `git` server.
+ ///
+ /// If not overridden, it defaults to the user agent provided by `curl`, which is a deviation from how `git` handles this.
+ /// Thus it's expected from the callers to set it to their application, or use higher-level crates which make it easy to do this
+ /// more correctly.
+ ///
+ /// Using the correct user-agent might affect how the server treats the request.
+ ///
+ /// Refers to `http.userAgent`.
+ pub user_agent: Option<String>,
+ /// The amount of time we wait until aborting a connection attempt.
+ ///
+ /// If `None`, this typically defaults to 2 minutes to 5 minutes.
+ /// Refers to `gitoxide.http.connectTimeout`.
+ pub connect_timeout: Option<std::time::Duration>,
+ /// If enabled, emit additional information about connections and possibly the data received or written.
+ pub verbose: bool,
+ /// If set, use this path to point to a file with CA certificates to verify peers.
+ pub ssl_ca_info: Option<PathBuf>,
+ /// The SSL version or version range to use, or `None` to let the TLS backend determine which versions are acceptable.
+ pub ssl_version: Option<SslVersionRangeInclusive>,
+ /// The HTTP version to enforce. If unset, it is implementation defined.
+ pub http_version: Option<HttpVersion>,
+ /// Backend specific options, if available.
+ pub backend: Option<Arc<Mutex<dyn Any + Send + Sync + 'static>>>,
+}
+
+/// The actual http client implementation, using curl
+#[cfg(feature = "http-client-curl")]
+pub type Impl = curl::Curl;
+/// The actual http client implementation, using reqwest
+#[cfg(feature = "http-client-reqwest")]
+pub type Impl = reqwest::Remote;
+
+/// A transport for supporting arbitrary http clients by abstracting interactions with them into the [Http] trait.
+pub struct Transport<H: Http> {
+ url: String,
+ user_agent_header: &'static str,
+ desired_version: Protocol,
+ actual_version: Protocol,
+ http: H,
+ service: Option<Service>,
+ line_provider: Option<gix_packetline::StreamingPeekableIter<H::ResponseBody>>,
+ identity: Option<gix_sec::identity::Account>,
+}
+
+impl<H: Http> Transport<H> {
+ /// Create a new instance with `http` as implementation to communicate to `url` using the given `desired_version`.
+ /// Note that we will always fallback to other versions as supported by the server.
+ pub fn new_http(http: H, url: &str, desired_version: Protocol) -> Self {
+ Transport {
+ url: url.to_owned(),
+ user_agent_header: concat!("User-Agent: git/oxide-", env!("CARGO_PKG_VERSION")),
+ desired_version,
+ actual_version: Default::default(),
+ service: None,
+ http,
+ line_provider: None,
+ identity: None,
+ }
+ }
+}
+
+#[cfg(any(feature = "http-client-curl", feature = "http-client-reqwest"))]
+impl Transport<Impl> {
+ /// Create a new instance to communicate to `url` using the given `desired_version` of the `git` protocol.
+ ///
+ /// Note that the actual implementation depends on feature toggles.
+ pub fn new(url: &str, desired_version: Protocol) -> Self {
+ Self::new_http(Impl::default(), url, desired_version)
+ }
+}
+
+impl<H: Http> Transport<H> {
+ fn check_content_type(service: Service, kind: &str, headers: <H as Http>::Headers) -> Result<(), client::Error> {
+ let wanted_content_type = format!("application/x-{}-{}", service.as_str(), kind);
+ if !headers.lines().collect::<Result<Vec<_>, _>>()?.iter().any(|l| {
+ let mut tokens = l.split(':');
+ tokens.next().zip(tokens.next()).map_or(false, |(name, value)| {
+ name.eq_ignore_ascii_case("content-type") && value.trim() == wanted_content_type
+ })
+ }) {
+ return Err(client::Error::Http(Error::Detail {
+ description: format!(
+ "Didn't find '{wanted_content_type}' header to indicate 'smart' protocol, and 'dumb' protocol is not supported."
+ ),
+ }));
+ }
+ Ok(())
+ }
+
+ #[allow(clippy::unnecessary_wraps, unknown_lints)]
+ fn add_basic_auth_if_present(&self, headers: &mut Vec<Cow<'_, str>>) -> Result<(), client::Error> {
+ if let Some(gix_sec::identity::Account { username, password }) = &self.identity {
+ #[cfg(not(debug_assertions))]
+ if self.url.starts_with("http://") {
+ return Err(client::Error::AuthenticationRefused(
+ "Will not send credentials in clear text over http",
+ ));
+ }
+ headers.push(Cow::Owned(format!(
+ "Authorization: Basic {}",
+ base64::engine::general_purpose::STANDARD.encode(format!("{username}:{password}"))
+ )))
+ }
+ Ok(())
+ }
+}
+
+fn append_url(base: &str, suffix: &str) -> String {
+ let mut buf = base.to_owned();
+ if base.as_bytes().last() != Some(&b'/') {
+ buf.push('/');
+ }
+ buf.push_str(suffix);
+ buf
+}
+
+impl<H: Http> client::TransportWithoutIO for Transport<H> {
+ fn set_identity(&mut self, identity: gix_sec::identity::Account) -> Result<(), client::Error> {
+ self.identity = Some(identity);
+ Ok(())
+ }
+
+ fn request(
+ &mut self,
+ write_mode: client::WriteMode,
+ on_into_read: MessageKind,
+ ) -> Result<RequestWriter<'_>, client::Error> {
+ let service = self.service.expect("handshake() must have been called first");
+ let url = append_url(&self.url, service.as_str());
+ let static_headers = &[
+ Cow::Borrowed(self.user_agent_header),
+ Cow::Owned(format!("Content-Type: application/x-{}-request", service.as_str())),
+ format!("Accept: application/x-{}-result", service.as_str()).into(),
+ ];
+ let mut dynamic_headers = Vec::new();
+ self.add_basic_auth_if_present(&mut dynamic_headers)?;
+ if self.actual_version != Protocol::V1 {
+ dynamic_headers.push(Cow::Owned(format!(
+ "Git-Protocol: version={}",
+ self.actual_version as usize
+ )));
+ }
+
+ let PostResponse {
+ headers,
+ body,
+ post_body,
+ } = self.http.post(
+ &url,
+ &self.url,
+ static_headers.iter().chain(&dynamic_headers),
+ write_mode.into(),
+ )?;
+ let line_provider = self
+ .line_provider
+ .as_mut()
+ .expect("handshake to have been called first");
+ line_provider.replace(body);
+ Ok(RequestWriter::new_from_bufread(
+ post_body,
+ Box::new(HeadersThenBody::<H, _> {
+ service,
+ headers: Some(headers),
+ body: line_provider.as_read_without_sidebands(),
+ }),
+ write_mode,
+ on_into_read,
+ ))
+ }
+
+ fn to_url(&self) -> Cow<'_, BStr> {
+ Cow::Borrowed(self.url.as_str().into())
+ }
+
+ fn connection_persists_across_multiple_requests(&self) -> bool {
+ false
+ }
+
+ fn configure(&mut self, config: &dyn Any) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
+ self.http.configure(config)
+ }
+}
+
+impl<H: Http> client::Transport for Transport<H> {
+ fn handshake<'a>(
+ &mut self,
+ service: Service,
+ extra_parameters: &'a [(&'a str, Option<&'a str>)],
+ ) -> Result<client::SetServiceResponse<'_>, client::Error> {
+ let url = append_url(self.url.as_ref(), &format!("info/refs?service={}", service.as_str()));
+ let static_headers = [Cow::Borrowed(self.user_agent_header)];
+ let mut dynamic_headers = Vec::<Cow<'_, str>>::new();
+ if self.desired_version != Protocol::V1 || !extra_parameters.is_empty() {
+ let mut parameters = if self.desired_version != Protocol::V1 {
+ let mut p = format!("version={}", self.desired_version as usize);
+ if !extra_parameters.is_empty() {
+ p.push(':');
+ }
+ p
+ } else {
+ String::new()
+ };
+ parameters.push_str(
+ &extra_parameters
+ .iter()
+ .map(|(key, value)| match value {
+ Some(value) => format!("{key}={value}"),
+ None => key.to_string(),
+ })
+ .collect::<Vec<_>>()
+ .join(":"),
+ );
+ dynamic_headers.push(format!("Git-Protocol: {parameters}").into());
+ }
+ self.add_basic_auth_if_present(&mut dynamic_headers)?;
+ let GetResponse { headers, body } =
+ self.http
+ .get(url.as_ref(), &self.url, static_headers.iter().chain(&dynamic_headers))?;
+ <Transport<H>>::check_content_type(service, "advertisement", headers)?;
+
+ let line_reader = self
+ .line_provider
+ .get_or_insert_with(|| gix_packetline::StreamingPeekableIter::new(body, &[PacketLineRef::Flush]));
+
+ // the service announcement is only sent sometimes depending on the exact server/protocol version/used protocol (http?)
+ // eat the announcement when its there to avoid errors later (and check that the correct service was announced).
+ // Ignore the announcement otherwise.
+ let line_ = line_reader
+ .peek_line()
+ .ok_or(client::Error::ExpectedLine("capabilities, version or service"))???;
+ let line = line_.as_text().ok_or(client::Error::ExpectedLine("text"))?;
+
+ if let Some(announced_service) = line.as_bstr().strip_prefix(b"# service=") {
+ if announced_service != service.as_str().as_bytes() {
+ return Err(client::Error::Http(Error::Detail {
+ description: format!(
+ "Expected to see service {:?}, but got {:?}",
+ service.as_str(),
+ announced_service
+ ),
+ }));
+ }
+
+ line_reader.as_read().read_to_end(&mut Vec::new())?;
+ }
+
+ let capabilities::recv::Outcome {
+ capabilities,
+ refs,
+ protocol: actual_protocol,
+ } = Capabilities::from_lines_with_version_detection(line_reader)?;
+ self.actual_version = actual_protocol;
+ self.service = Some(service);
+ Ok(client::SetServiceResponse {
+ actual_protocol,
+ capabilities,
+ refs,
+ })
+ }
+}
+
+struct HeadersThenBody<H: Http, B: Unpin> {
+ service: Service,
+ headers: Option<H::Headers>,
+ body: B,
+}
+
+impl<H: Http, B: Unpin> HeadersThenBody<H, B> {
+ fn handle_headers(&mut self) -> std::io::Result<()> {
+ if let Some(headers) = self.headers.take() {
+ <Transport<H>>::check_content_type(self.service, "result", headers)
+ .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?
+ }
+ Ok(())
+ }
+}
+
+impl<H: Http, B: Read + Unpin> Read for HeadersThenBody<H, B> {
+ fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+ self.handle_headers()?;
+ self.body.read(buf)
+ }
+}
+
+impl<H: Http, B: BufRead + Unpin> BufRead for HeadersThenBody<H, B> {
+ fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
+ self.handle_headers()?;
+ self.body.fill_buf()
+ }
+
+ fn consume(&mut self, amt: usize) {
+ self.body.consume(amt)
+ }
+}
+
+impl<H: Http, B: ReadlineBufRead + Unpin> ReadlineBufRead for HeadersThenBody<H, B> {
+ fn readline(&mut self) -> Option<std::io::Result<Result<PacketLineRef<'_>, gix_packetline::decode::Error>>> {
+ if let Err(err) = self.handle_headers() {
+ return Some(Err(err));
+ }
+ self.body.readline()
+ }
+}
+
+impl<H: Http, B: ExtendedBufRead + Unpin> ExtendedBufRead for HeadersThenBody<H, B> {
+ fn set_progress_handler(&mut self, handle_progress: Option<HandleProgress>) {
+ self.body.set_progress_handler(handle_progress)
+ }
+
+ fn peek_data_line(&mut self) -> Option<std::io::Result<Result<&[u8], client::Error>>> {
+ if let Err(err) = self.handle_headers() {
+ return Some(Err(err));
+ }
+ self.body.peek_data_line()
+ }
+
+ fn reset(&mut self, version: Protocol) {
+ self.body.reset(version)
+ }
+
+ fn stopped_at(&self) -> Option<MessageKind> {
+ self.body.stopped_at()
+ }
+}
+
+/// Connect to the given `url` via HTTP/S using the `desired_version` of the `git` protocol, with `http` as implementation.
+#[cfg(all(feature = "http-client", not(feature = "http-client-curl")))]
+pub fn connect_http<H: Http>(http: H, url: &str, desired_version: Protocol) -> Transport<H> {
+ Transport::new_http(http, url, desired_version)
+}
+
+/// Connect to the given `url` via HTTP/S using the `desired_version` of the `git` protocol.
+#[cfg(any(feature = "http-client-curl", feature = "http-client-reqwest"))]
+pub fn connect(url: &str, desired_version: Protocol) -> Transport<Impl> {
+ Transport::new(url, desired_version)
+}
+
+///
+#[cfg(feature = "http-client-curl")]
+pub mod redirect;
diff --git a/vendor/gix-transport/src/client/blocking_io/http/redirect.rs b/vendor/gix-transport/src/client/blocking_io/http/redirect.rs
new file mode 100644
index 000000000..f429961e0
--- /dev/null
+++ b/vendor/gix-transport/src/client/blocking_io/http/redirect.rs
@@ -0,0 +1,66 @@
+/// The error provided when redirection went beyond what we deem acceptable.
+#[derive(Debug, thiserror::Error)]
+#[error("Redirect url {redirect_url:?} could not be reconciled with original url {expected_url} as they don't share the same suffix")]
+pub struct Error {
+ redirect_url: String,
+ expected_url: String,
+}
+
+pub(crate) fn base_url(redirect_url: &str, base_url: &str, url: String) -> Result<String, Error> {
+ let tail = url
+ .strip_prefix(base_url)
+ .expect("BUG: caller assures `base_url` is subset of `url`");
+ redirect_url
+ .strip_suffix(tail)
+ .ok_or_else(|| Error {
+ redirect_url: redirect_url.into(),
+ expected_url: url,
+ })
+ .map(ToOwned::to_owned)
+}
+
+pub(crate) fn swap_tails(effective_base_url: Option<&str>, base_url: &str, mut url: String) -> String {
+ match effective_base_url {
+ Some(effective_base) => {
+ url.replace_range(..base_url.len(), effective_base);
+ url
+ }
+ None => url,
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn base_url_complete() {
+ assert_eq!(
+ base_url(
+ "https://redirected.org/b/info/refs?hi",
+ "https://original/a",
+ "https://original/a/info/refs?hi".into()
+ )
+ .unwrap(),
+ "https://redirected.org/b"
+ );
+ }
+
+ #[test]
+ fn swap_tails_complete() {
+ assert_eq!(
+ swap_tails(None, "not interesting", "used".into()),
+ "used",
+ "without effective base url, it passes url, no redirect happened yet"
+ );
+ assert_eq!(
+ swap_tails(
+ Some("https://redirected.org/b"),
+ "https://original/a",
+ "https://original/a/info/refs?something".into()
+ ),
+ "https://redirected.org/b/info/refs?something",
+ "the tail stays the same if redirection happened"
+ )
+ }
+}
diff --git a/vendor/gix-transport/src/client/blocking_io/http/reqwest/mod.rs b/vendor/gix-transport/src/client/blocking_io/http/reqwest/mod.rs
new file mode 100644
index 000000000..7c68b166e
--- /dev/null
+++ b/vendor/gix-transport/src/client/blocking_io/http/reqwest/mod.rs
@@ -0,0 +1,28 @@
+/// An implementation for HTTP requests via `reqwest`.
+pub struct Remote {
+ /// A worker thread which performs the actual request.
+ handle: Option<std::thread::JoinHandle<Result<(), remote::Error>>>,
+ /// A channel to send requests (work) to the worker thread.
+ request: std::sync::mpsc::SyncSender<remote::Request>,
+ /// A channel to receive the result of the prior request.
+ response: std::sync::mpsc::Receiver<remote::Response>,
+ /// A mechanism for configuring the remote.
+ config: crate::client::http::Options,
+}
+
+/// A function to configure a single request prior to sending it, support most complex configuration beyond what's possible with
+/// basic `git` http configuration.
+pub type ConfigureRequestFn = dyn FnMut(&mut reqwest::blocking::Request) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>>
+ + Send
+ + Sync
+ + 'static;
+
+/// Options to configure the reqwest HTTP handler.
+#[derive(Default)]
+pub struct Options {
+ /// A function to configure the request that is about to be made.
+ pub configure_request: Option<Box<ConfigureRequestFn>>,
+}
+
+///
+pub mod remote;
diff --git a/vendor/gix-transport/src/client/blocking_io/http/reqwest/remote.rs b/vendor/gix-transport/src/client/blocking_io/http/reqwest/remote.rs
new file mode 100644
index 000000000..724528ab9
--- /dev/null
+++ b/vendor/gix-transport/src/client/blocking_io/http/reqwest/remote.rs
@@ -0,0 +1,263 @@
+use std::{
+ any::Any,
+ convert::TryFrom,
+ io::{Read, Write},
+ str::FromStr,
+};
+
+use gix_features::io::pipe;
+
+use crate::client::{
+ http,
+ http::{reqwest::Remote, traits::PostBodyDataKind},
+};
+
+/// The error returned by the 'remote' helper, a purely internal construct to perform http requests.
+#[derive(Debug, thiserror::Error)]
+#[allow(missing_docs)]
+pub enum Error {
+ #[error(transparent)]
+ Reqwest(#[from] reqwest::Error),
+ #[error("Could not finish reading all data to post to the remote")]
+ ReadPostBody(#[from] std::io::Error),
+ #[error("Request configuration failed")]
+ ConfigureRequest(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
+}
+
+impl crate::IsSpuriousError for Error {
+ fn is_spurious(&self) -> bool {
+ match self {
+ Error::Reqwest(err) => {
+ err.is_timeout() || err.is_connect() || err.status().map_or(false, |status| status.is_server_error())
+ }
+ _ => false,
+ }
+ }
+}
+
+impl Default for Remote {
+ fn default() -> Self {
+ let (req_send, req_recv) = std::sync::mpsc::sync_channel(0);
+ let (res_send, res_recv) = std::sync::mpsc::sync_channel(0);
+ let handle = std::thread::spawn(move || -> Result<(), Error> {
+ // We may error while configuring, which is expected as part of the internal protocol. The error will be
+ // received and the sender of the request might restart us.
+ let client = reqwest::blocking::ClientBuilder::new()
+ .connect_timeout(std::time::Duration::from_secs(20))
+ .http1_title_case_headers()
+ .build()?;
+ for Request {
+ url,
+ headers,
+ upload_body_kind,
+ config,
+ } in req_recv
+ {
+ let mut req_builder = if upload_body_kind.is_some() {
+ client.post(url)
+ } else {
+ client.get(url)
+ }
+ .headers(headers);
+ let (post_body_tx, mut post_body_rx) = pipe::unidirectional(0);
+ let (mut response_body_tx, response_body_rx) = pipe::unidirectional(0);
+ let (mut headers_tx, headers_rx) = pipe::unidirectional(0);
+ if res_send
+ .send(Response {
+ headers: headers_rx,
+ body: response_body_rx,
+ upload_body: post_body_tx,
+ })
+ .is_err()
+ {
+ // This means our internal protocol is violated as the one who sent the request isn't listening anymore.
+ // Shut down as something is off.
+ break;
+ }
+ req_builder = match upload_body_kind {
+ Some(PostBodyDataKind::BoundedAndFitsIntoMemory) => {
+ let mut buf = Vec::<u8>::with_capacity(512);
+ post_body_rx.read_to_end(&mut buf)?;
+ req_builder.body(buf)
+ }
+ Some(PostBodyDataKind::Unbounded) => req_builder.body(reqwest::blocking::Body::new(post_body_rx)),
+ None => req_builder,
+ };
+ let mut req = req_builder.build()?;
+ if let Some(ref mut request_options) = config.backend.as_ref().and_then(|backend| backend.lock().ok()) {
+ if let Some(options) = request_options.downcast_mut::<super::Options>() {
+ if let Some(configure_request) = &mut options.configure_request {
+ configure_request(&mut req)?;
+ }
+ }
+ }
+ let mut res = match client.execute(req).and_then(|res| res.error_for_status()) {
+ Ok(res) => res,
+ Err(err) => {
+ let (kind, err) = match err.status() {
+ Some(status) => {
+ let kind = if status == reqwest::StatusCode::UNAUTHORIZED {
+ std::io::ErrorKind::PermissionDenied
+ } else if status.is_server_error() {
+ std::io::ErrorKind::ConnectionAborted
+ } else {
+ std::io::ErrorKind::Other
+ };
+ (kind, format!("Received HTTP status {}", status.as_str()))
+ }
+ None => (std::io::ErrorKind::Other, err.to_string()),
+ };
+ let err = Err(std::io::Error::new(kind, err));
+ headers_tx.channel.send(err).ok();
+ continue;
+ }
+ };
+
+ let send_headers = {
+ let headers = res.headers();
+ move || -> std::io::Result<()> {
+ for (name, value) in headers {
+ headers_tx.write_all(name.as_str().as_bytes())?;
+ headers_tx.write_all(b":")?;
+ headers_tx.write_all(value.as_bytes())?;
+ headers_tx.write_all(b"\n")?;
+ }
+ // Make sure this is an FnOnce closure to signal the remote reader we are done.
+ drop(headers_tx);
+ Ok(())
+ }
+ };
+
+ // We don't have to care if anybody is receiving the header, as a matter of fact we cannot fail sending them.
+ // Thus an error means the receiver failed somehow, but might also have decided not to read headers at all. Fine with us.
+ send_headers().ok();
+
+ // reading the response body is streaming and may fail for many reasons. If so, we send the error over the response
+ // body channel and that's all we can do.
+ if let Err(err) = std::io::copy(&mut res, &mut response_body_tx) {
+ response_body_tx.channel.send(Err(err)).ok();
+ }
+ }
+ Ok(())
+ });
+
+ Remote {
+ handle: Some(handle),
+ request: req_send,
+ response: res_recv,
+ config: http::Options::default(),
+ }
+ }
+}
+
+/// utilities
+impl Remote {
+ fn make_request(
+ &mut self,
+ url: &str,
+ _base_url: &str,
+ headers: impl IntoIterator<Item = impl AsRef<str>>,
+ upload_body_kind: Option<PostBodyDataKind>,
+ ) -> Result<http::PostResponse<pipe::Reader, pipe::Reader, pipe::Writer>, http::Error> {
+ let mut header_map = reqwest::header::HeaderMap::new();
+ for header_line in headers {
+ let header_line = header_line.as_ref();
+ let colon_pos = header_line
+ .find(':')
+ .expect("header line must contain a colon to separate key and value");
+ let header_name = &header_line[..colon_pos];
+ let value = &header_line[colon_pos + 1..];
+
+ match reqwest::header::HeaderName::from_str(header_name)
+ .ok()
+ .zip(reqwest::header::HeaderValue::try_from(value.trim()).ok())
+ {
+ Some((key, val)) => header_map.insert(key, val),
+ None => continue,
+ };
+ }
+ self.request
+ .send(Request {
+ url: url.to_owned(),
+ headers: header_map,
+ upload_body_kind,
+ config: self.config.clone(),
+ })
+ .expect("the remote cannot be down at this point");
+
+ let Response {
+ headers,
+ body,
+ upload_body,
+ } = match self.response.recv() {
+ Ok(res) => res,
+ Err(_) => {
+ let err = self
+ .handle
+ .take()
+ .expect("always present")
+ .join()
+ .expect("no panic")
+ .expect_err("no receiver means thread is down with init error");
+ *self = Self::default();
+ return Err(http::Error::InitHttpClient { source: Box::new(err) });
+ }
+ };
+
+ Ok(http::PostResponse {
+ post_body: upload_body,
+ headers,
+ body,
+ })
+ }
+}
+
+impl http::Http for Remote {
+ type Headers = pipe::Reader;
+ type ResponseBody = pipe::Reader;
+ type PostBody = pipe::Writer;
+
+ fn get(
+ &mut self,
+ url: &str,
+ base_url: &str,
+ headers: impl IntoIterator<Item = impl AsRef<str>>,
+ ) -> Result<http::GetResponse<Self::Headers, Self::ResponseBody>, http::Error> {
+ self.make_request(url, base_url, headers, None).map(Into::into)
+ }
+
+ fn post(
+ &mut self,
+ url: &str,
+ base_url: &str,
+ headers: impl IntoIterator<Item = impl AsRef<str>>,
+ post_body_kind: PostBodyDataKind,
+ ) -> Result<http::PostResponse<Self::Headers, Self::ResponseBody, Self::PostBody>, http::Error> {
+ self.make_request(url, base_url, headers, Some(post_body_kind))
+ }
+
+ fn configure(&mut self, config: &dyn Any) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
+ if let Some(config) = config.downcast_ref::<http::Options>() {
+ self.config = config.clone();
+ }
+ Ok(())
+ }
+}
+
+pub(crate) struct Request {
+ pub url: String,
+ pub headers: reqwest::header::HeaderMap,
+ pub upload_body_kind: Option<PostBodyDataKind>,
+ pub config: http::Options,
+}
+
+/// A link to a thread who provides data for the contained readers.
+/// The expected order is:
+/// - write `upload_body`
+/// - read `headers` to end
+/// - read `body` to hend
+pub(crate) struct Response {
+ pub headers: pipe::Reader,
+ pub body: pipe::Reader,
+ pub upload_body: pipe::Writer,
+}
diff --git a/vendor/gix-transport/src/client/blocking_io/http/traits.rs b/vendor/gix-transport/src/client/blocking_io/http/traits.rs
new file mode 100644
index 000000000..5b163f892
--- /dev/null
+++ b/vendor/gix-transport/src/client/blocking_io/http/traits.rs
@@ -0,0 +1,133 @@
+use crate::client::WriteMode;
+
+/// The error used by the [Http] trait.
+#[derive(Debug, thiserror::Error)]
+#[allow(missing_docs)]
+pub enum Error {
+ #[error("Could initialize the http client")]
+ InitHttpClient {
+ source: Box<dyn std::error::Error + Send + Sync + 'static>,
+ },
+ #[error("{description}")]
+ Detail { description: String },
+ #[error("An IO error occurred while uploading the body of a POST request")]
+ PostBody(#[from] std::io::Error),
+}
+
+impl crate::IsSpuriousError for Error {
+ fn is_spurious(&self) -> bool {
+ match self {
+ Error::PostBody(err) => err.is_spurious(),
+ #[cfg(any(feature = "http-client-reqwest", feature = "http-client-curl"))]
+ Error::InitHttpClient { source } => {
+ #[cfg(feature = "http-client-curl")]
+ if let Some(err) = source.downcast_ref::<crate::client::http::curl::Error>() {
+ return err.is_spurious();
+ };
+ #[cfg(feature = "http-client-reqwest")]
+ if let Some(err) = source.downcast_ref::<crate::client::http::reqwest::remote::Error>() {
+ return err.is_spurious();
+ };
+ false
+ }
+ _ => false,
+ }
+ }
+}
+
+/// The return value of [Http::get()].
+pub struct GetResponse<H, B> {
+ /// The response headers.
+ pub headers: H,
+ /// The response body.
+ pub body: B,
+}
+
+/// The return value of [Http::post()].
+pub struct PostResponse<H, B, PB> {
+ /// The body to post to the server as part of the request.
+ ///
+ /// **Note**: Implementations should drop the handle to avoid deadlocks.
+ pub post_body: PB,
+ /// The headers of the post response.
+ pub headers: H,
+ /// The body of the post response.
+ pub body: B,
+}
+
+/// Whether or not the post body is expected to fit into memory or not.
+#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
+pub enum PostBodyDataKind {
+ /// We know how much data we are sending and think it will fit into memory. This allows to collect it into a buffer
+ /// and send it with `Content-Length: <body-len>`.
+ BoundedAndFitsIntoMemory,
+ /// We don't know how much data we will send and assume it won't fit into memory. This enables streaming mode.
+ Unbounded,
+}
+
+impl From<WriteMode> for PostBodyDataKind {
+ fn from(m: WriteMode) -> Self {
+ match m {
+ WriteMode::Binary => PostBodyDataKind::Unbounded,
+ WriteMode::OneLfTerminatedLinePerWriteCall => PostBodyDataKind::BoundedAndFitsIntoMemory,
+ }
+ }
+}
+
+impl<A, B, C> From<PostResponse<A, B, C>> for GetResponse<A, B> {
+ fn from(v: PostResponse<A, B, C>) -> Self {
+ GetResponse {
+ headers: v.headers,
+ body: v.body,
+ }
+ }
+}
+
+/// A trait to abstract the HTTP operations needed to power all git interactions: read via GET and write via POST.
+/// Note that 401 must be turned into `std::io::Error(PermissionDenied)`, and other non-success http stati must be transformed
+/// into `std::io::Error(Other)`
+#[allow(clippy::type_complexity)]
+pub trait Http {
+ /// A type providing headers line by line.
+ type Headers: std::io::BufRead + Unpin;
+ /// A type providing the response.
+ type ResponseBody: std::io::BufRead;
+ /// A type allowing to write the content to post.
+ type PostBody: std::io::Write;
+
+ /// Initiate a `GET` request to `url` provided the given `headers`, where `base_url` is so that `base_url + tail == url`.
+ ///
+ /// The `base_url` helps to validate redirects and to swap it with the effective base after a redirect.
+ ///
+ /// The `headers` are provided verbatim and include both the key as well as the value.
+ fn get(
+ &mut self,
+ url: &str,
+ base_url: &str,
+ headers: impl IntoIterator<Item = impl AsRef<str>>,
+ ) -> Result<GetResponse<Self::Headers, Self::ResponseBody>, Error>;
+
+ /// Initiate a `POST` request to `url` providing with the given `headers`, where `base_url` is so that `base_url + tail == url`.
+ ///
+ /// The `base_url` helps to validate redirects and to swap it with the effective base after a redirect.
+ ///
+ /// The `headers` are provided verbatim and include both the key as well as the value.
+ /// Note that the [`PostResponse`] contains the [`post_body`][PostResponse::post_body] field which implements [`std::io::Write`]
+ /// and is expected to receive the body to post to the server. **It must be dropped** before reading the response
+ /// to prevent deadlocks.
+ fn post(
+ &mut self,
+ url: &str,
+ base_url: &str,
+ headers: impl IntoIterator<Item = impl AsRef<str>>,
+ body: PostBodyDataKind,
+ ) -> Result<PostResponse<Self::Headers, Self::ResponseBody, Self::PostBody>, Error>;
+
+ /// Pass `config` which can deserialize in the implementation's configuration, as documented separately.
+ ///
+ /// The caller must know how that `config` data looks like for the intended implementation.
+ fn configure(
+ &mut self,
+ config: &dyn std::any::Any,
+ ) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>>;
+}
diff --git a/vendor/gix-transport/src/client/blocking_io/mod.rs b/vendor/gix-transport/src/client/blocking_io/mod.rs
new file mode 100644
index 000000000..dfb3752af
--- /dev/null
+++ b/vendor/gix-transport/src/client/blocking_io/mod.rs
@@ -0,0 +1,20 @@
+///
+pub mod connect;
+
+///
+pub mod file;
+///
+#[cfg(feature = "http-client")]
+pub mod http;
+
+mod bufread_ext;
+pub use bufread_ext::{ExtendedBufRead, HandleProgress, ReadlineBufRead};
+
+mod request;
+pub use request::RequestWriter;
+
+///
+pub mod ssh;
+
+mod traits;
+pub use traits::{SetServiceResponse, Transport, TransportV2Ext};
diff --git a/vendor/gix-transport/src/client/blocking_io/request.rs b/vendor/gix-transport/src/client/blocking_io/request.rs
new file mode 100644
index 000000000..ae818eff3
--- /dev/null
+++ b/vendor/gix-transport/src/client/blocking_io/request.rs
@@ -0,0 +1,79 @@
+use std::io;
+
+use crate::client::{ExtendedBufRead, MessageKind, WriteMode};
+
+/// A [`Write`][io::Write] implementation optimized for writing packet lines.
+/// A type implementing `Write` for packet lines, which when done can be transformed into a `Read` for
+/// obtaining the response.
+pub struct RequestWriter<'a> {
+ on_into_read: MessageKind,
+ writer: gix_packetline::Writer<Box<dyn io::Write + 'a>>,
+ reader: Box<dyn ExtendedBufRead + Unpin + 'a>,
+}
+
+impl<'a> io::Write for RequestWriter<'a> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.writer.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.writer.flush()
+ }
+}
+
+/// methods with bonds to IO
+impl<'a> RequestWriter<'a> {
+ /// Create a new instance from a `writer` (commonly a socket), a `reader` into which to transform once the
+ /// writes are finished, along with configuration for the `write_mode` and information about which message to write
+ /// when this instance is converted into a `reader` to read the request's response.
+ pub fn new_from_bufread<W: io::Write + 'a>(
+ writer: W,
+ reader: Box<dyn ExtendedBufRead + Unpin + 'a>,
+ write_mode: WriteMode,
+ on_into_read: MessageKind,
+ ) -> Self {
+ let mut writer = gix_packetline::Writer::new(Box::new(writer) as Box<dyn io::Write>);
+ match write_mode {
+ WriteMode::Binary => writer.enable_binary_mode(),
+ WriteMode::OneLfTerminatedLinePerWriteCall => writer.enable_text_mode(),
+ }
+ RequestWriter {
+ on_into_read,
+ writer,
+ reader,
+ }
+ }
+
+ /// Write the given message as packet line.
+ pub fn write_message(&mut self, message: MessageKind) -> io::Result<()> {
+ match message {
+ MessageKind::Flush => gix_packetline::PacketLineRef::Flush.write_to(self.writer.inner_mut()),
+ MessageKind::Delimiter => gix_packetline::PacketLineRef::Delimiter.write_to(self.writer.inner_mut()),
+ MessageKind::ResponseEnd => gix_packetline::PacketLineRef::ResponseEnd.write_to(self.writer.inner_mut()),
+ MessageKind::Text(t) => gix_packetline::TextRef::from(t).write_to(self.writer.inner_mut()),
+ }
+ .map(|_| ())
+ }
+
+ /// Discard the ability to write and turn this instance into the reader for obtaining the other side's response.
+ ///
+ /// Doing so will also write the message type this instance was initialized with.
+ pub fn into_read(mut self) -> std::io::Result<Box<dyn ExtendedBufRead + Unpin + 'a>> {
+ self.write_message(self.on_into_read)?;
+ Ok(self.reader)
+ }
+
+ /// Dissolve this instance into its write and read handles without any message-writing side-effect as in [RequestWriter::into_read()].
+ ///
+ /// Furthermore, the writer will not encode everything it writes as packetlines, but write everything verbatim into the
+ /// underlying channel.
+ ///
+ /// # Note
+ ///
+ /// It's of utmost importance to drop the request writer before reading the response as these might be inter-dependent, depending on
+ /// the underlying transport mechanism. Failure to do so may result in a deadlock depending on how the write and read mechanism
+ /// is implemented.
+ pub fn into_parts(self) -> (Box<dyn io::Write + 'a>, Box<dyn ExtendedBufRead + Unpin + 'a>) {
+ (self.writer.into_inner(), self.reader)
+ }
+}
diff --git a/vendor/gix-transport/src/client/blocking_io/ssh/mod.rs b/vendor/gix-transport/src/client/blocking_io/ssh/mod.rs
new file mode 100644
index 000000000..63e4dc817
--- /dev/null
+++ b/vendor/gix-transport/src/client/blocking_io/ssh/mod.rs
@@ -0,0 +1,131 @@
+use std::process::Stdio;
+
+use crate::{client::blocking_io, Protocol};
+
+/// The error used in [`connect()`].
+#[derive(Debug, thiserror::Error)]
+#[allow(missing_docs)]
+pub enum Error {
+ #[error("The scheme in \"{}\" is not usable for an ssh connection", .0.to_bstring())]
+ UnsupportedScheme(gix_url::Url),
+}
+
+impl crate::IsSpuriousError for Error {}
+
+/// The kind of SSH programs we have built-in support for.
+///
+/// Various different programs exists with different capabilities, and we have a few built in.
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum ProgramKind {
+ /// The standard linux ssh program
+ Ssh,
+ /// The `(plink|putty).exe` binaries, typically only on windows.
+ Plink,
+ /// The `putty.exe` binary, typically only on windows.
+ Putty,
+ /// The `tortoiseplink.exe` binary, only on windows.
+ TortoisePlink,
+ /// A minimal ssh client that supports on options.
+ Simple,
+}
+
+mod program_kind;
+
+///
+pub mod invocation {
+ use std::ffi::OsString;
+
+ /// The error returned when producing ssh invocation arguments based on a selected invocation kind.
+ #[derive(Debug, thiserror::Error)]
+ #[error("The 'Simple' ssh variant doesn't support {function}")]
+ pub struct Error {
+ /// The simple command that should have been invoked.
+ pub command: OsString,
+ /// The function that was unsupported
+ pub function: &'static str,
+ }
+}
+
+///
+pub mod connect {
+ use std::ffi::{OsStr, OsString};
+
+ use crate::client::ssh::ProgramKind;
+
+ /// The options for use when [connecting][super::connect()] via the `ssh` protocol.
+ #[derive(Debug, Clone, Default)]
+ pub struct Options {
+ /// The program or script to use.
+ /// If unset, it defaults to `ssh` or `ssh.exe`, or the program implied by `kind` if that one is set.
+ pub command: Option<OsString>,
+ /// If `true`, a shell must not be used to execute `command`.
+ /// This defaults to `false`, and a shell can then be used if `command` seems to require it, but won't be
+ /// used unnecessarily.
+ pub disallow_shell: bool,
+ /// The ssh variant further identifying `program`. This determines which arguments will be used
+ /// when invoking the program.
+ /// If unset, the `program` basename determines the variant, or an invocation of the `command` itself.
+ pub kind: Option<ProgramKind>,
+ }
+
+ impl Options {
+ /// Return the configured ssh command, defaulting to `ssh` if neither the `command` nor the `kind` fields are set.
+ pub fn ssh_command(&self) -> &OsStr {
+ self.command
+ .as_deref()
+ .or_else(|| self.kind.and_then(|kind| kind.exe()))
+ .unwrap_or_else(|| OsStr::new("ssh"))
+ }
+ }
+}
+
+/// Connect to `host` using the ssh program to obtain data from the repository at `path` on the remote.
+///
+/// The optional `user` identifies the user's account to which to connect, while `port` allows to specify non-standard
+/// ssh ports.
+///
+/// The `desired_version` is the preferred protocol version when establishing the connection, but note that it can be
+/// downgraded by servers not supporting it.
+pub fn connect(
+ url: gix_url::Url,
+ desired_version: Protocol,
+ options: connect::Options,
+) -> Result<blocking_io::file::SpawnProcessOnDemand, Error> {
+ if url.scheme != gix_url::Scheme::Ssh || url.host().is_none() {
+ return Err(Error::UnsupportedScheme(url));
+ }
+ let ssh_cmd = options.ssh_command();
+ let mut kind = options.kind.unwrap_or_else(|| ProgramKind::from(ssh_cmd));
+ if options.kind.is_none() && kind == ProgramKind::Simple {
+ kind = if std::process::Command::from(
+ gix_command::prepare(ssh_cmd)
+ .stderr(Stdio::null())
+ .stdout(Stdio::null())
+ .stdin(Stdio::null())
+ .with_shell()
+ .arg("-G")
+ .arg(url.host().expect("always set for ssh urls")),
+ )
+ .status()
+ .ok()
+ .map_or(false, |status| status.success())
+ {
+ ProgramKind::Ssh
+ } else {
+ ProgramKind::Simple
+ };
+ }
+
+ let path = gix_url::expand_path::for_shell(url.path.clone());
+ Ok(blocking_io::file::SpawnProcessOnDemand::new_ssh(
+ url,
+ ssh_cmd,
+ path,
+ kind,
+ options.disallow_shell,
+ desired_version,
+ ))
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/vendor/gix-transport/src/client/blocking_io/ssh/program_kind.rs b/vendor/gix-transport/src/client/blocking_io/ssh/program_kind.rs
new file mode 100644
index 000000000..5e9d14a82
--- /dev/null
+++ b/vendor/gix-transport/src/client/blocking_io/ssh/program_kind.rs
@@ -0,0 +1,127 @@
+use std::{ffi::OsStr, io::ErrorKind};
+
+use bstr::{BString, ByteSlice, ByteVec};
+
+use crate::{
+ client::{ssh, ssh::ProgramKind},
+ Protocol,
+};
+
+impl ProgramKind {
+ /// Provide the name of the executable that belongs to this kind, or `None` if the kind is `Simple`.
+ pub fn exe(&self) -> Option<&'static OsStr> {
+ Some(OsStr::new(match self {
+ ProgramKind::Ssh => "ssh",
+ ProgramKind::Plink => "plink",
+ ProgramKind::Putty => "putty",
+ ProgramKind::TortoisePlink => "tortoiseplink.exe",
+ ProgramKind::Simple => return None,
+ }))
+ }
+
+ /// Prepare all information needed to invoke the ssh command
+ pub(crate) fn prepare_invocation(
+ &self,
+ ssh_cmd: &OsStr,
+ url: &gix_url::Url,
+ desired_version: Protocol,
+ disallow_shell: bool,
+ ) -> Result<gix_command::Prepare, ssh::invocation::Error> {
+ let mut prepare = gix_command::prepare(ssh_cmd).with_shell();
+ if disallow_shell {
+ prepare.use_shell = false;
+ }
+ let host = url.host().expect("present in ssh urls");
+ match self {
+ ProgramKind::Ssh => {
+ if desired_version != Protocol::V1 {
+ prepare = prepare
+ .args(["-o", "SendEnv=GIT_PROTOCOL"])
+ .env("GIT_PROTOCOL", format!("version={}", desired_version as usize))
+ }
+ if let Some(port) = url.port {
+ prepare = prepare.arg(format!("-p{port}"));
+ }
+ }
+ ProgramKind::Plink | ProgramKind::Putty | ProgramKind::TortoisePlink => {
+ if *self == ProgramKind::TortoisePlink {
+ prepare = prepare.arg("-batch");
+ }
+ if let Some(port) = url.port {
+ prepare = prepare.arg("-P");
+ prepare = prepare.arg(port.to_string());
+ }
+ }
+ ProgramKind::Simple => {
+ if url.port.is_some() {
+ return Err(ssh::invocation::Error {
+ command: ssh_cmd.into(),
+ function: "setting the port",
+ });
+ }
+ }
+ };
+ let host_as_ssh_arg = match url.user() {
+ Some(user) => format!("{user}@{host}"),
+ None => host.into(),
+ };
+
+ // Try to force ssh to yield english messages (for parsing later)
+ Ok(prepare.arg(host_as_ssh_arg).env("LANG", "C").env("LC_ALL", "C"))
+ }
+
+ /// Note that the caller has to assure that the ssh program is launched in English by setting the locale.
+ pub(crate) fn line_to_err(&self, line: BString) -> Result<std::io::Error, BString> {
+ let kind = match self {
+ ProgramKind::Ssh | ProgramKind::Simple => {
+ if line.contains_str(b"Permission denied") || line.contains_str(b"permission denied") {
+ Some(ErrorKind::PermissionDenied)
+ } else if line.contains_str(b"resolve hostname") {
+ Some(ErrorKind::ConnectionRefused)
+ } else if line.contains_str(b"connect to host")
+ || line.contains_str("Connection to ")
+ || line.contains_str("Connection closed by ")
+ {
+ // TODO: turn this into HostUnreachable when stable, or NetworkUnreachable in 'no route' example.
+ // It's important that it WON'T be considered spurious, but is considered a permanent failure.
+ Some(ErrorKind::NotFound)
+ } else {
+ None
+ }
+ }
+ ProgramKind::Plink | ProgramKind::Putty | ProgramKind::TortoisePlink => {
+ if line.contains_str(b"publickey") {
+ Some(ErrorKind::PermissionDenied)
+ } else {
+ None
+ }
+ }
+ };
+ match kind {
+ Some(kind) => Ok(std::io::Error::new(kind, Vec::from(line).into_string_lossy())),
+ None => Err(line),
+ }
+ }
+}
+
+impl<'a> From<&'a OsStr> for ProgramKind {
+ fn from(v: &'a OsStr) -> Self {
+ let p = std::path::Path::new(v);
+ match p.file_stem().and_then(|s| s.to_str()) {
+ None => ProgramKind::Simple,
+ Some(stem) => {
+ if stem.eq_ignore_ascii_case("ssh") {
+ ProgramKind::Ssh
+ } else if stem.eq_ignore_ascii_case("plink") {
+ ProgramKind::Plink
+ } else if stem.eq_ignore_ascii_case("putty") {
+ ProgramKind::Putty
+ } else if stem.eq_ignore_ascii_case("tortoiseplink") {
+ ProgramKind::TortoisePlink
+ } else {
+ ProgramKind::Simple
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/gix-transport/src/client/blocking_io/ssh/tests.rs b/vendor/gix-transport/src/client/blocking_io/ssh/tests.rs
new file mode 100644
index 000000000..27c661bd8
--- /dev/null
+++ b/vendor/gix-transport/src/client/blocking_io/ssh/tests.rs
@@ -0,0 +1,285 @@
+mod options {
+ mod ssh_command {
+ use crate::client::ssh::{connect::Options, ProgramKind};
+
+ #[test]
+ fn no_field_means_ssh() {
+ assert_eq!(Options::default().ssh_command(), "ssh");
+ }
+
+ #[test]
+ fn command_field_determines_ssh_command() {
+ assert_eq!(
+ Options {
+ command: Some("field-value".into()),
+ ..Default::default()
+ }
+ .ssh_command(),
+ "field-value"
+ );
+ assert_eq!(
+ Options {
+ command: Some("field-value".into()),
+ kind: Some(ProgramKind::TortoisePlink),
+ ..Default::default()
+ }
+ .ssh_command(),
+ "field-value"
+ );
+ }
+
+ #[test]
+ fn kind_serves_as_fallback() {
+ assert_eq!(
+ Options {
+ kind: Some(ProgramKind::TortoisePlink),
+ ..Default::default()
+ }
+ .ssh_command(),
+ "tortoiseplink.exe"
+ );
+ }
+ }
+}
+
+mod program_kind {
+ mod from_os_str {
+ use std::ffi::OsStr;
+
+ use crate::client::ssh::ProgramKind;
+
+ #[test]
+ fn known_variants_are_derived_from_basename() {
+ for name_or_path in [
+ "ssh",
+ "ssh.exe",
+ "SSH",
+ "SSH.exe",
+ "/bin/ssh",
+ "/bin/SSH",
+ #[cfg(windows)]
+ "c:\\bin\\ssh.exe",
+ ] {
+ assert_eq!(
+ ProgramKind::from(OsStr::new(name_or_path)),
+ ProgramKind::Ssh,
+ "{name_or_path:?} could not be identified correctly"
+ );
+ }
+ assert_eq!(
+ ProgramKind::from(OsStr::new("TortoisePlink.exe")),
+ ProgramKind::TortoisePlink
+ );
+ assert_eq!(ProgramKind::from(OsStr::new("putty")), ProgramKind::Putty);
+ assert_eq!(
+ ProgramKind::from(OsStr::new("../relative/Plink.exe")),
+ ProgramKind::Plink
+ );
+ }
+
+ #[test]
+ fn unknown_variants_fallback_to_simple() {
+ assert_eq!(
+ ProgramKind::from(OsStr::new("something-unknown-that-does-not-exist-for-sure-foobar")),
+ ProgramKind::Simple,
+ "in theory, we could fail right here but we don't and leave non-existing programs to fail during handshake"
+ );
+ }
+
+ #[test]
+ fn ssh_disguised_within_a_script_cannot_be_detected_due_to_invocation_with_dash_g() {
+ assert_eq!(
+ ProgramKind::from(OsStr::new("ssh -VVV")),
+ ProgramKind::Simple,
+ "we don't execute the command here but assume simple, even though we could determine it's ssh if we would do what git does here"
+ );
+ }
+ }
+
+ mod prepare_invocation {
+ use std::ffi::OsStr;
+
+ use crate::{
+ client::{ssh, ssh::ProgramKind},
+ Protocol,
+ };
+
+ #[test]
+ fn ssh() {
+ for (url, protocol, expected) in [
+ ("ssh://user@host:42/p", Protocol::V1, &["ssh", "-p42", "user@host"][..]),
+ ("ssh://user@host/p", Protocol::V1, &["ssh", "user@host"][..]),
+ ("ssh://host/p", Protocol::V1, &["ssh", "host"][..]),
+ (
+ "ssh://user@host:42/p",
+ Protocol::V2,
+ &["ssh", "-o", "SendEnv=GIT_PROTOCOL", "-p42", "user@host"][..],
+ ),
+ (
+ "ssh://user@host/p",
+ Protocol::V2,
+ &["ssh", "-o", "SendEnv=GIT_PROTOCOL", "user@host"][..],
+ ),
+ (
+ "ssh://host/p",
+ Protocol::V2,
+ &["ssh", "-o", "SendEnv=GIT_PROTOCOL", "host"][..],
+ ),
+ ] {
+ assert_eq!(call_args(ProgramKind::Ssh, url, protocol), quoted(expected));
+ }
+ }
+
+ #[test]
+ fn tortoise_plink_has_batch_command() {
+ assert_eq!(
+ call_args(ProgramKind::TortoisePlink, "ssh://user@host:42/p", Protocol::V2),
+ quoted(&["tortoiseplink.exe", "-batch", "-P", "42", "user@host"])
+ );
+ }
+
+ #[test]
+ fn port_for_all() {
+ for kind in [ProgramKind::TortoisePlink, ProgramKind::Plink, ProgramKind::Putty] {
+ assert!(call_args(kind, "ssh://user@host:43/p", Protocol::V2).ends_with(r#""-P" "43" "user@host""#));
+ }
+ }
+
+ #[test]
+ fn simple_cannot_handle_any_arguments() {
+ match try_call(ProgramKind::Simple, "ssh://user@host:42/p", Protocol::V2) {
+ Err(ssh::invocation::Error { .. }) => {}
+ _ => panic!("BUG: unexpected outcome"),
+ }
+ assert_eq!(
+ call_args(ProgramKind::Simple, "ssh://user@host/p", Protocol::V2),
+ quoted(&["simple", "user@host"]),
+ "simple can only do simple invocations"
+ );
+ }
+
+ #[test]
+ fn ssh_env_v2() {
+ let prepare = call(ProgramKind::Ssh, "ssh://host/p", Protocol::V2);
+ assert_eq!(
+ prepare.env,
+ &[
+ ("GIT_PROTOCOL".into(), "version=2".into()),
+ ("LANG".into(), "C".into()),
+ ("LC_ALL".into(), "C".into())
+ ]
+ );
+ assert!(!prepare.use_shell);
+ }
+
+ #[test]
+ fn disallow_shell_is_honored() -> Result {
+ let url = gix_url::parse("ssh://host/path".into()).expect("valid url");
+
+ let disallow_shell = false;
+ let prepare =
+ ProgramKind::Ssh.prepare_invocation(OsStr::new("echo hi"), &url, Protocol::V1, disallow_shell)?;
+ assert!(prepare.use_shell, "shells are used when needed");
+
+ let disallow_shell = true;
+ let prepare =
+ ProgramKind::Ssh.prepare_invocation(OsStr::new("echo hi"), &url, Protocol::V1, disallow_shell)?;
+ assert!(
+ !prepare.use_shell,
+ "but we can enforce it not to be used as well for historical reasons"
+ );
+ Ok(())
+ }
+
+ fn quoted(input: &[&str]) -> String {
+ input.iter().map(|s| format!("\"{s}\"")).collect::<Vec<_>>().join(" ")
+ }
+ fn try_call(
+ kind: ProgramKind,
+ url: &str,
+ version: Protocol,
+ ) -> std::result::Result<gix_command::Prepare, ssh::invocation::Error> {
+ let ssh_cmd = kind.exe().unwrap_or_else(|| OsStr::new("simple"));
+ let url = gix_url::parse(url.into()).expect("valid url");
+ kind.prepare_invocation(ssh_cmd, &url, version, false)
+ }
+ fn call(kind: ProgramKind, url: &str, version: Protocol) -> gix_command::Prepare {
+ try_call(kind, url, version).expect("no error")
+ }
+ fn call_args(kind: ProgramKind, url: &str, version: Protocol) -> String {
+ format!("{:?}", std::process::Command::from(call(kind, url, version)))
+ }
+
+ type Result = std::result::Result<(), ssh::invocation::Error>;
+ }
+
+ mod line_to_err {
+ use std::io::ErrorKind;
+
+ use crate::client::ssh::ProgramKind;
+
+ #[test]
+ fn all() {
+ for (kind, line, expected) in [
+ (
+ ProgramKind::Ssh,
+ "byron@github.com: Permission denied (publickey).",
+ ErrorKind::PermissionDenied,
+ ),
+ (
+ ProgramKind::Ssh,
+ "ssh: Could not resolve hostname hostfoobar: nodename nor servname provided, or not known",
+ ErrorKind::ConnectionRefused,
+ ),
+ (
+ ProgramKind::Ssh,
+ "ssh: connect to host example.org port 22: No route to host",
+ ErrorKind::NotFound,
+ ),
+ // connection closed by remote on windows
+ (
+ ProgramKind::Ssh,
+ "banner exchange: Connection to 127.0.0.1 port 61024: Software caused connection abort",
+ ErrorKind::NotFound,
+ ),
+ // connection closed by remote on unix
+ (
+ ProgramKind::Ssh,
+ "Connection closed by 127.0.0.1 port 8888", //
+ ErrorKind::NotFound,
+ ),
+ // this kind is basically unknown but we try our best, and simple equals ssh
+ (
+ ProgramKind::Simple,
+ "something permission denied something",
+ ErrorKind::PermissionDenied,
+ ),
+ (
+ ProgramKind::Simple,
+ "something resolve hostname hostfoobar: nodename nor servname something",
+ ErrorKind::ConnectionRefused,
+ ),
+ (
+ ProgramKind::Simple,
+ "something connect to host something",
+ ErrorKind::NotFound,
+ ),
+ ] {
+ assert_eq!(kind.line_to_err(line.into()).map(|err| err.kind()), Ok(expected));
+ }
+ }
+
+ #[test]
+ fn tortoiseplink_putty_plink() {
+ for kind in [ProgramKind::TortoisePlink, ProgramKind::Plink, ProgramKind::Putty] {
+ assert_eq!(
+ kind
+ .line_to_err("publickey".into())
+ .map(|err| err.kind()),
+ Ok(std::io::ErrorKind::PermissionDenied),
+ "this program pops up error messages in a window, no way to extract information from it. Maybe there is other ways to use it, 'publickey' they mention all"
+ );
+ }
+ }
+ }
+}
diff --git a/vendor/gix-transport/src/client/blocking_io/traits.rs b/vendor/gix-transport/src/client/blocking_io/traits.rs
new file mode 100644
index 000000000..7b2eaebbe
--- /dev/null
+++ b/vendor/gix-transport/src/client/blocking_io/traits.rs
@@ -0,0 +1,101 @@
+use std::{io::Write, ops::DerefMut};
+
+use bstr::BString;
+
+use crate::{
+ client::{Capabilities, Error, ExtendedBufRead, MessageKind, TransportWithoutIO, WriteMode},
+ Protocol, Service,
+};
+
+/// The response of the [`handshake()`][Transport::handshake()] method.
+pub struct SetServiceResponse<'a> {
+ /// The protocol the service can provide. May be different from the requested one
+ pub actual_protocol: Protocol,
+ /// The capabilities parsed from the server response.
+ pub capabilities: Capabilities,
+ /// In protocol version one, this is set to a list of refs and their peeled counterparts.
+ pub refs: Option<Box<dyn crate::client::ReadlineBufRead + 'a>>,
+}
+
+/// All methods provided here must be called in the correct order according to the [communication protocol][Protocol]
+/// used to connect to them.
+/// It does, however, know just enough to be able to provide a higher-level interface than would otherwise be possible.
+/// Thus the consumer of this trait will not have to deal with packet lines at all.
+/// **Note that** whenever a `Read` trait or `Write` trait is produced, it must be exhausted.
+pub trait Transport: TransportWithoutIO {
+ /// Initiate connection to the given service and send the given `extra_parameters` along with it.
+ ///
+ /// `extra_parameters` are interpreted as `key=value` pairs if the second parameter is `Some` or as `key`
+ /// if it is None.
+ ///
+ /// Returns the service capabilities according according to the actual [Protocol] it supports,
+ /// and possibly a list of refs to be obtained.
+ /// This means that asking for an unsupported protocol might result in a protocol downgrade to the given one
+ /// if [TransportWithoutIO::supported_protocol_versions()] includes it.
+ /// Exhaust the returned [BufReader][SetServiceResponse::refs] for a list of references in case of protocol V1
+ /// before making another request.
+ fn handshake<'a>(
+ &mut self,
+ service: Service,
+ extra_parameters: &'a [(&'a str, Option<&'a str>)],
+ ) -> Result<SetServiceResponse<'_>, Error>;
+}
+
+// Would be nice if the box implementation could auto-forward to all implemented traits.
+impl<T: Transport + ?Sized> Transport for Box<T> {
+ fn handshake<'a>(
+ &mut self,
+ service: Service,
+ extra_parameters: &'a [(&'a str, Option<&'a str>)],
+ ) -> Result<SetServiceResponse<'_>, Error> {
+ self.deref_mut().handshake(service, extra_parameters)
+ }
+}
+
+impl<T: Transport + ?Sized> Transport for &mut T {
+ fn handshake<'a>(
+ &mut self,
+ service: Service,
+ extra_parameters: &'a [(&'a str, Option<&'a str>)],
+ ) -> Result<SetServiceResponse<'_>, Error> {
+ self.deref_mut().handshake(service, extra_parameters)
+ }
+}
+
+/// An extension trait to add more methods to everything implementing [`Transport`].
+pub trait TransportV2Ext {
+ /// Invoke a protocol V2 style `command` with given `capabilities` and optional command specific `arguments`.
+ /// The `capabilities` were communicated during the handshake.
+ /// _Note:_ panics if [handshake][Transport::handshake()] wasn't performed beforehand.
+ fn invoke<'a>(
+ &mut self,
+ command: &str,
+ capabilities: impl Iterator<Item = (&'a str, Option<impl AsRef<str>>)> + 'a,
+ arguments: Option<impl Iterator<Item = bstr::BString>>,
+ ) -> Result<Box<dyn ExtendedBufRead + Unpin + '_>, Error>;
+}
+
+impl<T: Transport> TransportV2Ext for T {
+ fn invoke<'a>(
+ &mut self,
+ command: &str,
+ capabilities: impl Iterator<Item = (&'a str, Option<impl AsRef<str>>)> + 'a,
+ arguments: Option<impl Iterator<Item = BString>>,
+ ) -> Result<Box<dyn ExtendedBufRead + Unpin + '_>, Error> {
+ let mut writer = self.request(WriteMode::OneLfTerminatedLinePerWriteCall, MessageKind::Flush)?;
+ writer.write_all(format!("command={command}").as_bytes())?;
+ for (name, value) in capabilities {
+ match value {
+ Some(value) => writer.write_all(format!("{name}={}", value.as_ref()).as_bytes()),
+ None => writer.write_all(name.as_bytes()),
+ }?;
+ }
+ if let Some(arguments) = arguments {
+ writer.write_message(MessageKind::Delimiter)?;
+ for argument in arguments {
+ writer.write_all(argument.as_ref())?;
+ }
+ }
+ Ok(writer.into_read()?)
+ }
+}
diff --git a/vendor/gix-transport/src/client/capabilities.rs b/vendor/gix-transport/src/client/capabilities.rs
new file mode 100644
index 000000000..4c10dc100
--- /dev/null
+++ b/vendor/gix-transport/src/client/capabilities.rs
@@ -0,0 +1,307 @@
+use bstr::{BStr, BString, ByteSlice};
+
+#[cfg(any(feature = "blocking-client", feature = "async-client"))]
+use crate::client;
+use crate::Protocol;
+
+/// The error used in [`Capabilities::from_bytes()`] and [`Capabilities::from_lines()`].
+#[derive(Debug, thiserror::Error)]
+#[allow(missing_docs)]
+pub enum Error {
+ #[error("Capabilities were missing entirely as there was no 0 byte")]
+ MissingDelimitingNullByte,
+ #[error("there was not a single capability behind the delimiter")]
+ NoCapabilities,
+ #[error("a version line was expected, but none was retrieved")]
+ MissingVersionLine,
+ #[error("expected 'version X', got {0:?}")]
+ MalformattedVersionLine(BString),
+ #[error("Got unsupported version {actual:?}, expected {}", *desired as u8)]
+ UnsupportedVersion { desired: Protocol, actual: BString },
+ #[error("An IO error occurred while reading V2 lines")]
+ Io(#[from] std::io::Error),
+}
+
+/// A structure to represent multiple [capabilities][Capability] or features supported by the server.
+#[derive(Debug, Clone, Default)]
+#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
+pub struct Capabilities {
+ data: BString,
+ value_sep: u8,
+}
+
+/// The name of a single capability.
+pub struct Capability<'a>(&'a BStr);
+
+impl<'a> Capability<'a> {
+ /// Returns the name of the capability.
+ ///
+ /// Most capabilities only consist of a name, making them appear like a feature toggle.
+ pub fn name(&self) -> &'a BStr {
+ self.0
+ .splitn(2, |b| *b == b'=')
+ .next()
+ .expect("there is always a single item")
+ .as_bstr()
+ }
+ /// Returns the value associated with the capability.
+ ///
+ /// Note that the caller must know whether a single or multiple values are expected, in which
+ /// case [`values()`][Capability::values()] should be called.
+ pub fn value(&self) -> Option<&'a BStr> {
+ self.0.splitn(2, |b| *b == b'=').nth(1).map(|s| s.as_bstr())
+ }
+ /// Returns the values of a capability if its [`value()`][Capability::value()] is space separated.
+ pub fn values(&self) -> Option<impl Iterator<Item = &'a BStr>> {
+ self.value().map(|v| v.split(|b| *b == b' ').map(|s| s.as_bstr()))
+ }
+ /// Returns true if its space-separated [`value()`][Capability::value()] contains the given `want`ed capability.
+ pub fn supports(&self, want: impl Into<&'a BStr>) -> Option<bool> {
+ let want = want.into();
+ self.values().map(|mut iter| iter.any(|v| v == want))
+ }
+}
+
+impl Capabilities {
+ /// Parse capabilities from the given `bytes`.
+ ///
+ /// Useful in case they are encoded within a `ref` behind a null byte.
+ pub fn from_bytes(bytes: &[u8]) -> Result<(Capabilities, usize), Error> {
+ let delimiter_pos = bytes.find_byte(0).ok_or(Error::MissingDelimitingNullByte)?;
+ if delimiter_pos + 1 == bytes.len() {
+ return Err(Error::NoCapabilities);
+ }
+ let capabilities = &bytes[delimiter_pos + 1..];
+ Ok((
+ Capabilities {
+ data: capabilities.as_bstr().to_owned(),
+ value_sep: b' ',
+ },
+ delimiter_pos,
+ ))
+ }
+
+ /// Parse capabilities from the given a `lines_buf` which is expected to be all newline separated lines
+ /// from the server.
+ ///
+ /// Useful for parsing capabilities from a data sent from a server, and to avoid having to deal with
+ /// blocking and async traits for as long as possible. There is no value in parsing a few bytes
+ /// in a non-blocking fashion.
+ pub fn from_lines(lines_buf: BString) -> Result<Capabilities, Error> {
+ let mut lines = <_ as bstr::ByteSlice>::lines(lines_buf.as_slice().trim());
+ let version_line = lines.next().ok_or(Error::MissingVersionLine)?;
+ let (name, value) = version_line.split_at(
+ version_line
+ .find(b" ")
+ .ok_or_else(|| Error::MalformattedVersionLine(version_line.to_owned().into()))?,
+ );
+ if name != b"version" {
+ return Err(Error::MalformattedVersionLine(version_line.to_owned().into()));
+ }
+ if value != b" 2" {
+ return Err(Error::UnsupportedVersion {
+ desired: Protocol::V2,
+ actual: value.to_owned().into(),
+ });
+ }
+ Ok(Capabilities {
+ value_sep: b'\n',
+ data: lines.as_bytes().into(),
+ })
+ }
+
+ /// Returns true of the given `feature` is mentioned in this list of capabilities.
+ pub fn contains(&self, feature: &str) -> bool {
+ self.capability(feature).is_some()
+ }
+
+ /// Returns the capability with `name`.
+ pub fn capability(&self, name: &str) -> Option<Capability<'_>> {
+ self.iter().find(|c| c.name() == name.as_bytes().as_bstr())
+ }
+
+ /// Returns an iterator over all capabilities.
+ pub fn iter(&self) -> impl Iterator<Item = Capability<'_>> {
+ self.data
+ .split(move |b| *b == self.value_sep)
+ .map(|c| Capability(c.as_bstr()))
+ }
+}
+
+/// internal use
+#[cfg(any(feature = "blocking-client", feature = "async-client"))]
+impl Capabilities {
+ fn extract_protocol(capabilities_or_version: gix_packetline::TextRef<'_>) -> Result<Protocol, client::Error> {
+ let line = capabilities_or_version.as_bstr();
+ let version = if line.starts_with_str("version ") {
+ if line.len() != "version X".len() {
+ return Err(client::Error::UnsupportedProtocolVersion(line.as_bstr().into()));
+ }
+ match line {
+ line if line.ends_with_str("1") => Protocol::V1,
+ line if line.ends_with_str("2") => Protocol::V2,
+ _ => return Err(client::Error::UnsupportedProtocolVersion(line.as_bstr().into())),
+ }
+ } else {
+ Protocol::V1
+ };
+ Ok(version)
+ }
+}
+
+#[cfg(feature = "blocking-client")]
+///
+pub mod recv {
+ use std::io;
+
+ use bstr::ByteVec;
+
+ use crate::{client, client::Capabilities, Protocol};
+
+ /// Success outcome of [`Capabilities::from_lines_with_version_detection`].
+ pub struct Outcome<'a> {
+ /// The [`Capabilities`] the remote advertised.
+ pub capabilities: Capabilities,
+ /// The remote refs as a [`io::BufRead`].
+ ///
+ /// This is `Some` only when protocol v1 is used. The [`io::BufRead`] must be exhausted by
+ /// the caller.
+ pub refs: Option<Box<dyn crate::client::ReadlineBufRead + 'a>>,
+ /// The [`Protocol`] the remote advertised.
+ pub protocol: Protocol,
+ }
+
+ impl Capabilities {
+ /// Read the capabilities and version advertisement from the given packetline reader.
+ ///
+ /// If [`Protocol::V1`] was requested, or the remote decided to downgrade, the remote refs
+ /// advertisement will also be included in the [`Outcome`].
+ pub fn from_lines_with_version_detection<T: io::Read>(
+ rd: &mut gix_packetline::StreamingPeekableIter<T>,
+ ) -> Result<Outcome<'_>, client::Error> {
+ // NOTE that this is vitally important - it is turned on and stays on for all following requests so
+ // we automatically abort if the server sends an ERR line anywhere.
+ // We are sure this can't clash with binary data when sent due to the way the PACK
+ // format looks like, thus there is no binary blob that could ever look like an ERR line by accident.
+ rd.fail_on_err_lines(true);
+
+ let line = rd
+ .peek_line()
+ .ok_or(client::Error::ExpectedLine("capabilities or version"))???;
+ let line = line.as_text().ok_or(client::Error::ExpectedLine("text"))?;
+
+ let version = Capabilities::extract_protocol(line)?;
+ match version {
+ Protocol::V1 => {
+ let (capabilities, delimiter_position) = Capabilities::from_bytes(line.0)?;
+ rd.peek_buffer_replace_and_truncate(delimiter_position, b'\n');
+ Ok(Outcome {
+ capabilities,
+ refs: Some(Box::new(rd.as_read())),
+ protocol: Protocol::V1,
+ })
+ }
+ Protocol::V2 => Ok(Outcome {
+ capabilities: {
+ let mut rd = rd.as_read();
+ let mut buf = Vec::new();
+ while let Some(line) = rd.read_data_line() {
+ let line = line??;
+ match line.as_bstr() {
+ Some(line) => {
+ buf.push_str(line);
+ if buf.last() != Some(&b'\n') {
+ buf.push(b'\n');
+ }
+ }
+ None => break,
+ }
+ }
+ Capabilities::from_lines(buf.into())?
+ },
+ refs: None,
+ protocol: Protocol::V2,
+ }),
+ }
+ }
+ }
+}
+
+#[cfg(feature = "async-client")]
+#[allow(missing_docs)]
+///
+pub mod recv {
+ use bstr::ByteVec;
+ use futures_io::AsyncRead;
+
+ use crate::{client, client::Capabilities, Protocol};
+
+ /// Success outcome of [`Capabilities::from_lines_with_version_detection`].
+ pub struct Outcome<'a> {
+ /// The [`Capabilities`] the remote advertised.
+ pub capabilities: Capabilities,
+ /// The remote refs as an [`AsyncBufRead`].
+ ///
+ /// This is `Some` only when protocol v1 is used. The [`AsyncBufRead`] must be exhausted by
+ /// the caller.
+ pub refs: Option<Box<dyn crate::client::ReadlineBufRead + Unpin + 'a>>,
+ /// The [`Protocol`] the remote advertised.
+ pub protocol: Protocol,
+ }
+
+ impl Capabilities {
+ /// Read the capabilities and version advertisement from the given packetline reader.
+ ///
+ /// If [`Protocol::V1`] was requested, or the remote decided to downgrade, the remote refs
+ /// advertisement will also be included in the [`Outcome`].
+ pub async fn from_lines_with_version_detection<T: AsyncRead + Unpin>(
+ rd: &mut gix_packetline::StreamingPeekableIter<T>,
+ ) -> Result<Outcome<'_>, client::Error> {
+ // NOTE that this is vitally important - it is turned on and stays on for all following requests so
+ // we automatically abort if the server sends an ERR line anywhere.
+ // We are sure this can't clash with binary data when sent due to the way the PACK
+ // format looks like, thus there is no binary blob that could ever look like an ERR line by accident.
+ rd.fail_on_err_lines(true);
+
+ let line = rd
+ .peek_line()
+ .await
+ .ok_or(client::Error::ExpectedLine("capabilities or version"))???;
+ let line = line.as_text().ok_or(client::Error::ExpectedLine("text"))?;
+
+ let version = Capabilities::extract_protocol(line)?;
+ match version {
+ Protocol::V1 => {
+ let (capabilities, delimiter_position) = Capabilities::from_bytes(line.0)?;
+ rd.peek_buffer_replace_and_truncate(delimiter_position, b'\n');
+ Ok(Outcome {
+ capabilities,
+ refs: Some(Box::new(rd.as_read())),
+ protocol: Protocol::V1,
+ })
+ }
+ Protocol::V2 => Ok(Outcome {
+ capabilities: {
+ let mut rd = rd.as_read();
+ let mut buf = Vec::new();
+ while let Some(line) = rd.read_data_line().await {
+ let line = line??;
+ match line.as_bstr() {
+ Some(line) => {
+ buf.push_str(line);
+ if buf.last() != Some(&b'\n') {
+ buf.push(b'\n');
+ }
+ }
+ None => break,
+ }
+ }
+ Capabilities::from_lines(buf.into())?
+ },
+ refs: None,
+ protocol: Protocol::V2,
+ }),
+ }
+ }
+ }
+}
diff --git a/vendor/gix-transport/src/client/git/async_io.rs b/vendor/gix-transport/src/client/git/async_io.rs
new file mode 100644
index 000000000..00bea41a3
--- /dev/null
+++ b/vendor/gix-transport/src/client/git/async_io.rs
@@ -0,0 +1,151 @@
+use std::{borrow::Cow, error::Error};
+
+use async_trait::async_trait;
+use bstr::{BStr, BString, ByteVec};
+use futures_io::{AsyncRead, AsyncWrite};
+use futures_lite::AsyncWriteExt;
+use gix_packetline::PacketLineRef;
+
+use crate::{
+ client::{self, capabilities, git, Capabilities, SetServiceResponse},
+ Protocol, Service,
+};
+
+impl<R, W> client::TransportWithoutIO for git::Connection<R, W>
+where
+ R: AsyncRead + Unpin,
+ W: AsyncWrite + Unpin,
+{
+ fn request(
+ &mut self,
+ write_mode: client::WriteMode,
+ on_into_read: client::MessageKind,
+ ) -> Result<client::RequestWriter<'_>, client::Error> {
+ Ok(client::RequestWriter::new_from_bufread(
+ &mut self.writer,
+ Box::new(self.line_provider.as_read_without_sidebands()),
+ write_mode,
+ on_into_read,
+ ))
+ }
+ fn to_url(&self) -> Cow<'_, BStr> {
+ self.custom_url.as_ref().map_or_else(
+ || {
+ let mut possibly_lossy_url = self.path.clone();
+ possibly_lossy_url.insert_str(0, "file://");
+ Cow::Owned(possibly_lossy_url)
+ },
+ |url| Cow::Borrowed(url.as_ref()),
+ )
+ }
+
+ fn connection_persists_across_multiple_requests(&self) -> bool {
+ true
+ }
+
+ fn configure(&mut self, _config: &dyn std::any::Any) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
+ Ok(())
+ }
+}
+
+#[async_trait(?Send)]
+impl<R, W> client::Transport for git::Connection<R, W>
+where
+ R: AsyncRead + Unpin,
+ W: AsyncWrite + Unpin,
+{
+ async fn handshake<'a>(
+ &mut self,
+ service: Service,
+ extra_parameters: &'a [(&'a str, Option<&'a str>)],
+ ) -> Result<SetServiceResponse<'_>, client::Error> {
+ if self.mode == git::ConnectMode::Daemon {
+ let mut line_writer = gix_packetline::Writer::new(&mut self.writer).binary_mode();
+ line_writer
+ .write_all(&git::message::connect(
+ service,
+ self.desired_version,
+ &self.path,
+ self.virtual_host.as_ref(),
+ extra_parameters,
+ ))
+ .await?;
+ line_writer.flush().await?;
+ }
+
+ let capabilities::recv::Outcome {
+ capabilities,
+ refs,
+ protocol: actual_protocol,
+ } = Capabilities::from_lines_with_version_detection(&mut self.line_provider).await?;
+ Ok(SetServiceResponse {
+ actual_protocol,
+ capabilities,
+ refs,
+ })
+ }
+}
+
+impl<R, W> git::Connection<R, W>
+where
+ R: AsyncRead + Unpin,
+ W: AsyncWrite + Unpin,
+{
+ /// Create a connection from the given `read` and `write`, asking for `desired_version` as preferred protocol
+ /// and the transfer of the repository at `repository_path`.
+ ///
+ /// `virtual_host` along with a port to which to connect to, while `mode` determines the kind of endpoint to connect to.
+ pub fn new(
+ read: R,
+ write: W,
+ desired_version: Protocol,
+ repository_path: impl Into<BString>,
+ virtual_host: Option<(impl Into<String>, Option<u16>)>,
+ mode: git::ConnectMode,
+ ) -> Self {
+ git::Connection {
+ writer: write,
+ line_provider: gix_packetline::StreamingPeekableIter::new(read, &[PacketLineRef::Flush]),
+ path: repository_path.into(),
+ virtual_host: virtual_host.map(|(h, p)| (h.into(), p)),
+ desired_version,
+ custom_url: None,
+ mode,
+ }
+ }
+}
+
+#[cfg(feature = "async-std")]
+mod async_net {
+ use std::time::Duration;
+
+ use async_std::net::TcpStream;
+
+ use crate::client::{git, Error};
+
+ impl git::Connection<TcpStream, TcpStream> {
+ /// Create a new TCP connection using the `git` protocol of `desired_version`, and make a connection to `host`
+ /// at `port` for accessing the repository at `path` on the server side.
+ pub async fn new_tcp(
+ host: &str,
+ port: Option<u16>,
+ path: bstr::BString,
+ desired_version: crate::Protocol,
+ ) -> Result<git::Connection<TcpStream, TcpStream>, Error> {
+ let read = async_std::io::timeout(
+ Duration::from_secs(5),
+ TcpStream::connect(&(host, port.unwrap_or(9418))),
+ )
+ .await?;
+ let write = read.clone();
+ Ok(git::Connection::new(
+ read,
+ write,
+ desired_version,
+ path,
+ None::<(String, _)>,
+ git::ConnectMode::Daemon,
+ ))
+ }
+ }
+}
diff --git a/vendor/gix-transport/src/client/git/blocking_io.rs b/vendor/gix-transport/src/client/git/blocking_io.rs
new file mode 100644
index 000000000..8087b8aea
--- /dev/null
+++ b/vendor/gix-transport/src/client/git/blocking_io.rs
@@ -0,0 +1,199 @@
+use std::{any::Any, borrow::Cow, error::Error, io::Write};
+
+use bstr::{BStr, BString, ByteVec};
+use gix_packetline::PacketLineRef;
+
+use crate::{
+ client::{self, capabilities, git, Capabilities, SetServiceResponse},
+ Protocol, Service,
+};
+
+impl<R, W> client::TransportWithoutIO for git::Connection<R, W>
+where
+ R: std::io::Read,
+ W: std::io::Write,
+{
+ fn request(
+ &mut self,
+ write_mode: client::WriteMode,
+ on_into_read: client::MessageKind,
+ ) -> Result<client::RequestWriter<'_>, client::Error> {
+ Ok(client::RequestWriter::new_from_bufread(
+ &mut self.writer,
+ Box::new(self.line_provider.as_read_without_sidebands()),
+ write_mode,
+ on_into_read,
+ ))
+ }
+
+ fn to_url(&self) -> Cow<'_, BStr> {
+ self.custom_url.as_ref().map_or_else(
+ || {
+ let mut possibly_lossy_url = self.path.clone();
+ possibly_lossy_url.insert_str(0, "file://");
+ Cow::Owned(possibly_lossy_url)
+ },
+ |url| Cow::Borrowed(url.as_ref()),
+ )
+ }
+
+ fn connection_persists_across_multiple_requests(&self) -> bool {
+ true
+ }
+
+ fn configure(&mut self, _config: &dyn Any) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
+ Ok(())
+ }
+}
+
+impl<R, W> client::Transport for git::Connection<R, W>
+where
+ R: std::io::Read,
+ W: std::io::Write,
+{
+ fn handshake<'a>(
+ &mut self,
+ service: Service,
+ extra_parameters: &'a [(&'a str, Option<&'a str>)],
+ ) -> Result<SetServiceResponse<'_>, client::Error> {
+ if self.mode == git::ConnectMode::Daemon {
+ let mut line_writer = gix_packetline::Writer::new(&mut self.writer).binary_mode();
+ line_writer.write_all(&git::message::connect(
+ service,
+ self.desired_version,
+ &self.path,
+ self.virtual_host.as_ref(),
+ extra_parameters,
+ ))?;
+ line_writer.flush()?;
+ }
+
+ let capabilities::recv::Outcome {
+ capabilities,
+ refs,
+ protocol: actual_protocol,
+ } = Capabilities::from_lines_with_version_detection(&mut self.line_provider)?;
+ Ok(SetServiceResponse {
+ actual_protocol,
+ capabilities,
+ refs,
+ })
+ }
+}
+
+impl<R, W> git::Connection<R, W>
+where
+ R: std::io::Read,
+ W: std::io::Write,
+{
+ /// Create a connection from the given `read` and `write`, asking for `desired_version` as preferred protocol
+ /// and the transfer of the repository at `repository_path`.
+ ///
+ /// `virtual_host` along with a port to which to connect to, while `mode` determines the kind of endpoint to connect to.
+ pub fn new(
+ read: R,
+ write: W,
+ desired_version: Protocol,
+ repository_path: impl Into<BString>,
+ virtual_host: Option<(impl Into<String>, Option<u16>)>,
+ mode: git::ConnectMode,
+ ) -> Self {
+ git::Connection {
+ writer: write,
+ line_provider: gix_packetline::StreamingPeekableIter::new(read, &[PacketLineRef::Flush]),
+ path: repository_path.into(),
+ virtual_host: virtual_host.map(|(h, p)| (h.into(), p)),
+ desired_version,
+ custom_url: None,
+ mode,
+ }
+ }
+ pub(crate) fn new_for_spawned_process(
+ reader: R,
+ writer: W,
+ desired_version: Protocol,
+ repository_path: impl Into<BString>,
+ ) -> Self {
+ Self::new(
+ reader,
+ writer,
+ desired_version,
+ repository_path,
+ None::<(&str, _)>,
+ git::ConnectMode::Process,
+ )
+ }
+}
+
+///
+pub mod connect {
+ use std::net::{TcpStream, ToSocketAddrs};
+
+ use bstr::BString;
+
+ use crate::client::git;
+ /// The error used in [`connect()`].
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error("An IO error occurred when connecting to the server")]
+ Io(#[from] std::io::Error),
+ #[error("Could not parse {host:?} as virtual host with format <host>[:port]")]
+ VirtualHostInvalid { host: String },
+ }
+
+ impl crate::IsSpuriousError for Error {
+ fn is_spurious(&self) -> bool {
+ match self {
+ Error::Io(err) => err.is_spurious(),
+ _ => false,
+ }
+ }
+ }
+
+ fn parse_host(input: String) -> Result<(String, Option<u16>), Error> {
+ let mut tokens = input.splitn(2, ':');
+ Ok(match (tokens.next(), tokens.next()) {
+ (Some(host), None) => (host.to_owned(), None),
+ (Some(host), Some(port)) => (
+ host.to_owned(),
+ Some(port.parse().map_err(|_| Error::VirtualHostInvalid { host: input })?),
+ ),
+ _ => unreachable!("we expect at least one token, the original string"),
+ })
+ }
+
+ /// Connect to a git daemon running on `host` and optionally `port` and a repository at `path`.
+ ///
+ /// Use `desired_version` to specify a preferred protocol to use, knowing that it can be downgraded by a server not supporting it.
+ pub fn connect(
+ host: &str,
+ path: BString,
+ desired_version: crate::Protocol,
+ port: Option<u16>,
+ ) -> Result<git::Connection<TcpStream, TcpStream>, Error> {
+ let read = TcpStream::connect_timeout(
+ &(host, port.unwrap_or(9418))
+ .to_socket_addrs()?
+ .next()
+ .expect("after successful resolution there is an IP address"),
+ std::time::Duration::from_secs(5),
+ )?;
+ let write = read.try_clone()?;
+ let vhost = std::env::var("GIT_OVERRIDE_VIRTUAL_HOST")
+ .ok()
+ .map(parse_host)
+ .transpose()?
+ .unwrap_or_else(|| (host.to_owned(), port));
+ Ok(git::Connection::new(
+ read,
+ write,
+ desired_version,
+ path,
+ Some(vhost),
+ git::ConnectMode::Daemon,
+ ))
+ }
+}
+
+pub use connect::connect;
diff --git a/vendor/gix-transport/src/client/git/mod.rs b/vendor/gix-transport/src/client/git/mod.rs
new file mode 100644
index 000000000..2b950b44a
--- /dev/null
+++ b/vendor/gix-transport/src/client/git/mod.rs
@@ -0,0 +1,177 @@
+use bstr::BString;
+
+use crate::Protocol;
+
+/// The way to connect to a process speaking the `git` protocol.
+#[derive(PartialEq, Eq, Clone, Copy)]
+pub enum ConnectMode {
+ /// A git daemon.
+ Daemon,
+ /// A spawned `git` process to upload a pack to the client.
+ Process,
+}
+
+/// A TCP connection to either a `git` daemon or a spawned `git` process.
+///
+/// When connecting to a daemon, additional context information is sent with the first line of the handshake. Otherwise that
+/// context is passed using command line arguments to a [spawned `git` process][crate::client::file::SpawnProcessOnDemand].
+pub struct Connection<R, W> {
+ pub(in crate::client) writer: W,
+ pub(in crate::client) line_provider: gix_packetline::StreamingPeekableIter<R>,
+ pub(in crate::client) path: BString,
+ pub(in crate::client) virtual_host: Option<(String, Option<u16>)>,
+ pub(in crate::client) desired_version: Protocol,
+ custom_url: Option<BString>,
+ pub(in crate::client) mode: ConnectMode,
+}
+
+impl<R, W> Connection<R, W> {
+ /// Return the inner reader and writer
+ pub fn into_inner(self) -> (R, W) {
+ (self.line_provider.into_inner(), self.writer)
+ }
+
+ /// Optionally set the URL to be returned when asked for it if `Some` or calculate a default for `None`.
+ ///
+ /// The URL is required as parameter for authentication helpers which are called in transports
+ /// that support authentication. Even though plain git transports don't support that, this
+ /// may well be the case in custom transports.
+ pub fn custom_url(mut self, url: Option<BString>) -> Self {
+ self.custom_url = url;
+ self
+ }
+}
+
+mod message {
+ use bstr::{BString, ByteVec};
+
+ use crate::{Protocol, Service};
+
+ pub fn connect(
+ service: Service,
+ desired_version: Protocol,
+ path: &[u8],
+ virtual_host: Option<&(String, Option<u16>)>,
+ extra_parameters: &[(&str, Option<&str>)],
+ ) -> BString {
+ let mut out = bstr::BString::from(service.as_str());
+ out.push(b' ');
+ let path = gix_url::expand_path::for_shell(path.into());
+ out.extend_from_slice(&path);
+ out.push(0);
+ if let Some((host, port)) = virtual_host {
+ out.push_str("host=");
+ out.extend_from_slice(host.as_bytes());
+ if let Some(port) = port {
+ out.push_byte(b':');
+ out.push_str(&format!("{port}"));
+ }
+ out.push(0);
+ }
+ // We only send the version when needed, as otherwise a V2 server who is asked for V1 will respond with 'version 1'
+ // as extra lines in the reply, which we don't want to handle. Especially since an old server will not respond with that
+ // line (is what I assume, at least), so it's an optional part in the response to understand and handle. There is no value
+ // in that, so let's help V2 servers to respond in a way that assumes V1.
+ let extra_params_need_null_prefix = if desired_version != Protocol::V1 {
+ out.push(0);
+ out.push_str(format!("version={}", desired_version as usize));
+ out.push(0);
+ false
+ } else {
+ true
+ };
+
+ if !extra_parameters.is_empty() {
+ if extra_params_need_null_prefix {
+ out.push(0);
+ }
+ for (key, value) in extra_parameters {
+ match value {
+ Some(value) => out.push_str(format!("{key}={value}")),
+ None => out.push_str(key),
+ }
+ out.push(0);
+ }
+ }
+ out
+ }
+ #[cfg(test)]
+ mod tests {
+ use crate::{client::git, Protocol, Service};
+
+ #[test]
+ fn version_1_without_host_and_version() {
+ assert_eq!(
+ git::message::connect(Service::UploadPack, Protocol::V1, b"hello/world", None, &[]),
+ "git-upload-pack hello/world\0"
+ )
+ }
+ #[test]
+ fn version_2_without_host_and_version() {
+ assert_eq!(
+ git::message::connect(Service::UploadPack, Protocol::V2, b"hello\\world", None, &[]),
+ "git-upload-pack hello\\world\0\0version=2\0"
+ )
+ }
+ #[test]
+ fn version_2_without_host_and_version_and_exta_parameters() {
+ assert_eq!(
+ git::message::connect(
+ Service::UploadPack,
+ Protocol::V2,
+ b"/path/project.git",
+ None,
+ &[("key", Some("value")), ("value-only", None)]
+ ),
+ "git-upload-pack /path/project.git\0\0version=2\0key=value\0value-only\0"
+ )
+ }
+ #[test]
+ fn with_host_without_port() {
+ assert_eq!(
+ git::message::connect(
+ Service::UploadPack,
+ Protocol::V1,
+ b"hello\\world",
+ Some(&("host".into(), None)),
+ &[]
+ ),
+ "git-upload-pack hello\\world\0host=host\0"
+ )
+ }
+ #[test]
+ fn with_host_without_port_and_extra_parameters() {
+ assert_eq!(
+ git::message::connect(
+ Service::UploadPack,
+ Protocol::V1,
+ b"hello\\world",
+ Some(&("host".into(), None)),
+ &[("key", Some("value")), ("value-only", None)]
+ ),
+ "git-upload-pack hello\\world\0host=host\0\0key=value\0value-only\0"
+ )
+ }
+ #[test]
+ fn with_host_with_port() {
+ assert_eq!(
+ git::message::connect(
+ Service::UploadPack,
+ Protocol::V1,
+ b"hello\\world",
+ Some(&("host".into(), Some(404))),
+ &[]
+ ),
+ "git-upload-pack hello\\world\0host=host:404\0"
+ )
+ }
+ }
+}
+
+#[cfg(feature = "async-client")]
+mod async_io;
+
+#[cfg(feature = "blocking-client")]
+mod blocking_io;
+#[cfg(feature = "blocking-client")]
+pub use blocking_io::connect;
diff --git a/vendor/gix-transport/src/client/mod.rs b/vendor/gix-transport/src/client/mod.rs
new file mode 100644
index 000000000..b5296543e
--- /dev/null
+++ b/vendor/gix-transport/src/client/mod.rs
@@ -0,0 +1,36 @@
+#[cfg(feature = "async-client")]
+mod async_io;
+#[cfg(feature = "async-client")]
+pub use async_io::{
+ connect, ExtendedBufRead, HandleProgress, ReadlineBufRead, RequestWriter, SetServiceResponse, Transport,
+ TransportV2Ext,
+};
+
+mod traits;
+pub use traits::TransportWithoutIO;
+
+#[cfg(feature = "blocking-client")]
+mod blocking_io;
+#[cfg(feature = "http-client")]
+pub use blocking_io::http;
+#[cfg(feature = "blocking-client")]
+pub use blocking_io::{
+ connect, file, ssh, ExtendedBufRead, HandleProgress, ReadlineBufRead, RequestWriter, SetServiceResponse, Transport,
+ TransportV2Ext,
+};
+#[cfg(feature = "blocking-client")]
+#[doc(inline)]
+pub use connect::function::connect;
+
+///
+pub mod capabilities;
+#[doc(inline)]
+pub use capabilities::Capabilities;
+
+mod non_io_types;
+pub use gix_sec::identity::Account;
+pub use non_io_types::{Error, MessageKind, WriteMode};
+
+///
+#[cfg(any(feature = "blocking-client", feature = "async-client"))]
+pub mod git;
diff --git a/vendor/gix-transport/src/client/non_io_types.rs b/vendor/gix-transport/src/client/non_io_types.rs
new file mode 100644
index 000000000..a207f7f0b
--- /dev/null
+++ b/vendor/gix-transport/src/client/non_io_types.rs
@@ -0,0 +1,159 @@
+/// Configure how the [`RequestWriter`][crate::client::RequestWriter] behaves when writing bytes.
+#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
+#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
+pub enum WriteMode {
+ /// Each [write()][std::io::Write::write()] call writes the bytes verbatim as one or more packet lines.
+ ///
+ /// This mode also indicates to the transport that it should try to stream data as it is unbounded. This mode is typically used
+ /// for sending packs whose exact size is not necessarily known in advance.
+ Binary,
+ /// Each [write()][std::io::Write::write()] call assumes text in the input, assures a trailing newline and writes it as single packet line.
+ ///
+ /// This mode also indicates that the lines written fit into memory, hence the transport may chose to not stream it but to buffer it
+ /// instead. This is relevant for some transports, like the one for HTTP.
+ OneLfTerminatedLinePerWriteCall,
+}
+
+impl Default for WriteMode {
+ fn default() -> Self {
+ WriteMode::OneLfTerminatedLinePerWriteCall
+ }
+}
+
+/// The kind of packet line to write when transforming a [`RequestWriter`][crate::client::RequestWriter] into an
+/// [`ExtendedBufRead`][crate::client::ExtendedBufRead].
+#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
+#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
+pub enum MessageKind {
+ /// A `flush` packet.
+ Flush,
+ /// A V2 delimiter.
+ Delimiter,
+ /// The end of a response.
+ ResponseEnd,
+ /// The given text.
+ Text(&'static [u8]),
+}
+
+#[cfg(any(feature = "blocking-client", feature = "async-client"))]
+pub(crate) mod connect {
+ /// Options for connecting to a remote.
+ #[derive(Debug, Default, Clone)]
+ pub struct Options {
+ /// Use `version` to set the desired protocol version to use when connecting, but note that the server may downgrade it.
+ pub version: crate::Protocol,
+ #[cfg(feature = "blocking-client")]
+ /// Options to use if the scheme of the URL is `ssh`.
+ pub ssh: crate::client::ssh::connect::Options,
+ }
+
+ /// The error used in [`connect()`][crate::connect()].
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error(transparent)]
+ Url(#[from] gix_url::parse::Error),
+ #[error("The git repository path could not be converted to UTF8")]
+ PathConversion(#[from] bstr::Utf8Error),
+ #[error("connection failed")]
+ Connection(#[from] Box<dyn std::error::Error + Send + Sync>),
+ #[error("The url {url:?} contains information that would not be used by the {scheme} protocol")]
+ UnsupportedUrlTokens {
+ url: bstr::BString,
+ scheme: gix_url::Scheme,
+ },
+ #[error("The '{0}' protocol is currently unsupported")]
+ UnsupportedScheme(gix_url::Scheme),
+ #[cfg(not(any(feature = "http-client-curl", feature = "http-client-reqwest")))]
+ #[error(
+ "'{0}' is not compiled in. Compile with the 'http-client-curl' or 'http-client-reqwest' cargo feature"
+ )]
+ CompiledWithoutHttp(gix_url::Scheme),
+ }
+
+ // TODO: maybe fix this workaround: want `IsSpuriousError` in `Connection(…)`
+ impl crate::IsSpuriousError for Error {
+ fn is_spurious(&self) -> bool {
+ match self {
+ Error::Connection(err) => {
+ #[cfg(feature = "blocking-client")]
+ if let Some(err) = err.downcast_ref::<crate::client::git::connect::Error>() {
+ return err.is_spurious();
+ };
+ if let Some(err) = err.downcast_ref::<crate::client::Error>() {
+ return err.is_spurious();
+ }
+ false
+ }
+ _ => false,
+ }
+ }
+ }
+}
+
+mod error {
+ use std::ffi::OsString;
+
+ use bstr::BString;
+
+ use crate::client::capabilities;
+ #[cfg(feature = "http-client")]
+ use crate::client::http;
+ #[cfg(feature = "blocking-client")]
+ use crate::client::ssh;
+
+ #[cfg(feature = "http-client")]
+ type HttpError = http::Error;
+ #[cfg(feature = "blocking-client")]
+ type SshInvocationError = ssh::invocation::Error;
+ #[cfg(not(feature = "http-client"))]
+ type HttpError = std::convert::Infallible;
+ #[cfg(not(feature = "blocking-client"))]
+ type SshInvocationError = std::convert::Infallible;
+
+ /// The error used in most methods of the [`client`][crate::client] module
+ #[derive(thiserror::Error, Debug)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error("An IO error occurred when talking to the server")]
+ Io(#[from] std::io::Error),
+ #[error("Capabilities could not be parsed")]
+ Capabilities {
+ #[from]
+ err: capabilities::Error,
+ },
+ #[error("A packet line could not be decoded")]
+ LineDecode {
+ #[from]
+ err: gix_packetline::decode::Error,
+ },
+ #[error("A {0} line was expected, but there was none")]
+ ExpectedLine(&'static str),
+ #[error("Expected a data line, but got a delimiter")]
+ ExpectedDataLine,
+ #[error("The transport layer does not support authentication")]
+ AuthenticationUnsupported,
+ #[error("The transport layer refuses to use a given identity: {0}")]
+ AuthenticationRefused(&'static str),
+ #[error("The protocol version indicated by {:?} is unsupported", {0})]
+ UnsupportedProtocolVersion(BString),
+ #[error("Failed to invoke program {command:?}")]
+ InvokeProgram { source: std::io::Error, command: OsString },
+ #[error(transparent)]
+ Http(#[from] HttpError),
+ #[error(transparent)]
+ SshInvocation(SshInvocationError),
+ }
+
+ impl crate::IsSpuriousError for Error {
+ fn is_spurious(&self) -> bool {
+ match self {
+ Error::Io(err) => err.is_spurious(),
+ Error::Http(err) => err.is_spurious(),
+ _ => false,
+ }
+ }
+ }
+}
+
+pub use error::Error;
diff --git a/vendor/gix-transport/src/client/traits.rs b/vendor/gix-transport/src/client/traits.rs
new file mode 100644
index 000000000..408935391
--- /dev/null
+++ b/vendor/gix-transport/src/client/traits.rs
@@ -0,0 +1,115 @@
+use std::{
+ any::Any,
+ borrow::Cow,
+ ops::{Deref, DerefMut},
+};
+
+use bstr::BStr;
+
+#[cfg(any(feature = "blocking-client", feature = "async-client"))]
+use crate::client::{MessageKind, RequestWriter, WriteMode};
+use crate::{client::Error, Protocol};
+
+/// This trait represents all transport related functions that don't require any input/output to be done which helps
+/// implementation to share more code across blocking and async programs.
+pub trait TransportWithoutIO {
+ /// If the handshake or subsequent reads failed with [std::io::ErrorKind::PermissionDenied], use this method to
+ /// inform the transport layer about the identity to use for subsequent calls.
+ /// If authentication continues to fail even with an identity set, consider communicating this to the provider
+ /// of the identity in order to mark it as invalid. Otherwise the user might have difficulty updating obsolete
+ /// credentials.
+ /// Please note that most transport layers are unauthenticated and thus return [an error][Error::AuthenticationUnsupported] here.
+ fn set_identity(&mut self, _identity: gix_sec::identity::Account) -> Result<(), Error> {
+ Err(Error::AuthenticationUnsupported)
+ }
+ /// Get a writer for sending data and obtaining the response. It can be configured in various ways
+ /// to support the task at hand.
+ /// `write_mode` determines how calls to the `write(…)` method are interpreted, and `on_into_read` determines
+ /// which message to write when the writer is turned into the response reader using [`into_read()`][RequestWriter::into_read()].
+ #[cfg(any(feature = "blocking-client", feature = "async-client"))]
+ fn request(&mut self, write_mode: WriteMode, on_into_read: MessageKind) -> Result<RequestWriter<'_>, Error>;
+
+ /// Returns the canonical URL pointing to the destination of this transport.
+ fn to_url(&self) -> Cow<'_, BStr>;
+
+ /// If the actually advertised server version is contained in the returned slice or it is empty, continue as normal,
+ /// assume the server's protocol version is desired or acceptable.
+ ///
+ /// Otherwise, abort the fetch operation with an error to avoid continuing any interaction with the transport.
+ ///
+ /// In V1 this means a potentially large list of advertised refs won't be read, instead the connection is ignored
+ /// leaving the server with a client who potentially unexpectedly terminated the connection.
+ ///
+ /// Note that `transport.close()` is not called explicitly.
+ ///
+ /// Custom transports can override this to prevent any use of older protocol versions.
+ fn supported_protocol_versions(&self) -> &[Protocol] {
+ &[]
+ }
+
+ /// Returns true if the transport provides persistent connections across multiple requests, or false otherwise.
+ /// Not being persistent implies that certain information has to be resent on each 'turn'
+ /// of the fetch negotiation or that the end of interaction (i.e. no further request will be made) has to be indicated
+ /// to the server for most graceful termination of the connection.
+ fn connection_persists_across_multiple_requests(&self) -> bool;
+
+ /// Pass `config` can be cast and interpreted by the implementation, as documented separately.
+ ///
+ /// The caller must know how that `config` data looks like for the intended implementation.
+ fn configure(&mut self, config: &dyn Any) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>>;
+}
+
+// Would be nice if the box implementation could auto-forward to all implemented traits.
+impl<T: TransportWithoutIO + ?Sized> TransportWithoutIO for Box<T> {
+ fn set_identity(&mut self, identity: gix_sec::identity::Account) -> Result<(), Error> {
+ self.deref_mut().set_identity(identity)
+ }
+
+ #[cfg(any(feature = "blocking-client", feature = "async-client"))]
+ fn request(&mut self, write_mode: WriteMode, on_into_read: MessageKind) -> Result<RequestWriter<'_>, Error> {
+ self.deref_mut().request(write_mode, on_into_read)
+ }
+
+ fn to_url(&self) -> Cow<'_, BStr> {
+ self.deref().to_url()
+ }
+
+ fn supported_protocol_versions(&self) -> &[Protocol] {
+ self.deref().supported_protocol_versions()
+ }
+
+ fn connection_persists_across_multiple_requests(&self) -> bool {
+ self.deref().connection_persists_across_multiple_requests()
+ }
+
+ fn configure(&mut self, config: &dyn Any) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
+ self.deref_mut().configure(config)
+ }
+}
+
+impl<T: TransportWithoutIO + ?Sized> TransportWithoutIO for &mut T {
+ fn set_identity(&mut self, identity: gix_sec::identity::Account) -> Result<(), Error> {
+ self.deref_mut().set_identity(identity)
+ }
+
+ #[cfg(any(feature = "blocking-client", feature = "async-client"))]
+ fn request(&mut self, write_mode: WriteMode, on_into_read: MessageKind) -> Result<RequestWriter<'_>, Error> {
+ self.deref_mut().request(write_mode, on_into_read)
+ }
+
+ fn to_url(&self) -> Cow<'_, BStr> {
+ self.deref().to_url()
+ }
+
+ fn supported_protocol_versions(&self) -> &[Protocol] {
+ self.deref().supported_protocol_versions()
+ }
+
+ fn connection_persists_across_multiple_requests(&self) -> bool {
+ self.deref().connection_persists_across_multiple_requests()
+ }
+
+ fn configure(&mut self, config: &dyn Any) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
+ self.deref_mut().configure(config)
+ }
+}
diff --git a/vendor/gix-transport/src/lib.rs b/vendor/gix-transport/src/lib.rs
new file mode 100644
index 000000000..ca02b3b59
--- /dev/null
+++ b/vendor/gix-transport/src/lib.rs
@@ -0,0 +1,99 @@
+//! An implementation of the `git` transport layer, abstracting over all of its [versions][Protocol], providing
+//! [`connect()`] to establish a connection given a repository URL.
+//!
+//! All git transports are supported, including `ssh`, `git`, `http` and `https`, as well as local repository paths.
+//! ## 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)]
+#![forbid(unsafe_code)]
+
+#[cfg(feature = "async-trait")]
+pub use async_trait;
+pub use bstr;
+#[cfg(feature = "futures-io")]
+pub use futures_io;
+pub use gix_packetline as packetline;
+
+/// The version of the way client and server communicate.
+#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
+#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
+#[allow(missing_docs)]
+pub enum Protocol {
+ /// Version 1 was the first one conceived, is stateful, and our implementation was seen to cause deadlocks. Prefer V2
+ V1 = 1,
+ /// A command-based and stateless protocol with clear semantics, and the one to use assuming the server isn't very old.
+ /// This is the default.
+ V2 = 2,
+}
+
+impl Default for Protocol {
+ fn default() -> Self {
+ Protocol::V2
+ }
+}
+
+/// The kind of service to invoke on the client or the server side.
+#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
+#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
+pub enum Service {
+ /// The service sending packs from a server to the client. Used for fetching pack data.
+ UploadPack,
+ /// The service receiving packs produced by the client, who sends a pack to the server.
+ ReceivePack,
+}
+
+impl Service {
+ /// Render this instance as string recognized by the git transport layer.
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ Service::ReceivePack => "git-receive-pack",
+ Service::UploadPack => "git-upload-pack",
+ }
+ }
+}
+
+mod traits {
+ use std::convert::Infallible;
+
+ /// An error which can tell whether it's worth retrying to maybe succeed next time.
+ pub trait IsSpuriousError: std::error::Error {
+ /// Return `true` if retrying might result in a different outcome due to IO working out differently.
+ fn is_spurious(&self) -> bool {
+ false
+ }
+ }
+
+ impl IsSpuriousError for Infallible {}
+
+ impl IsSpuriousError for std::io::Error {
+ fn is_spurious(&self) -> bool {
+ // TODO: also include the new special Kinds (currently unstable)
+ use std::io::ErrorKind::*;
+ match self.kind() {
+ Unsupported | WriteZero | InvalidInput | InvalidData | WouldBlock | AlreadyExists
+ | AddrNotAvailable | NotConnected | Other | PermissionDenied | NotFound => false,
+ Interrupted | UnexpectedEof | OutOfMemory | TimedOut | BrokenPipe | AddrInUse | ConnectionAborted
+ | ConnectionReset | ConnectionRefused => true,
+ _ => false,
+ }
+ }
+ }
+}
+pub use traits::IsSpuriousError;
+
+///
+pub mod client;
+
+#[doc(inline)]
+#[cfg(any(
+ feature = "blocking-client",
+ all(feature = "async-client", any(feature = "async-std"))
+))]
+pub use client::connect;
+
+#[cfg(all(feature = "async-client", feature = "blocking-client"))]
+compile_error!("Cannot set both 'blocking-client' and 'async-client' features as they are mutually exclusive");