From 10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 14:41:41 +0200 Subject: Merging upstream version 1.70.0+dfsg2. Signed-off-by: Daniel Baumann --- vendor/gix-transport/.cargo-checksum.json | 1 + vendor/gix-transport/CHANGELOG.md | 1494 ++++++++++++++++++++ vendor/gix-transport/Cargo.toml | 175 +++ .../src/client/async_io/bufread_ext.rs | 126 ++ .../gix-transport/src/client/async_io/connect.rs | 47 + vendor/gix-transport/src/client/async_io/mod.rs | 13 + .../gix-transport/src/client/async_io/request.rs | 103 ++ vendor/gix-transport/src/client/async_io/traits.rs | 113 ++ .../src/client/blocking_io/bufread_ext.rs | 114 ++ .../src/client/blocking_io/connect.rs | 68 + .../gix-transport/src/client/blocking_io/file.rs | 288 ++++ .../src/client/blocking_io/http/curl/mod.rs | 169 +++ .../src/client/blocking_io/http/curl/remote.rs | 392 +++++ .../src/client/blocking_io/http/mod.rs | 512 +++++++ .../src/client/blocking_io/http/redirect.rs | 66 + .../src/client/blocking_io/http/reqwest/mod.rs | 28 + .../src/client/blocking_io/http/reqwest/remote.rs | 263 ++++ .../src/client/blocking_io/http/traits.rs | 133 ++ vendor/gix-transport/src/client/blocking_io/mod.rs | 20 + .../src/client/blocking_io/request.rs | 79 ++ .../src/client/blocking_io/ssh/mod.rs | 131 ++ .../src/client/blocking_io/ssh/program_kind.rs | 127 ++ .../src/client/blocking_io/ssh/tests.rs | 285 ++++ .../gix-transport/src/client/blocking_io/traits.rs | 101 ++ vendor/gix-transport/src/client/capabilities.rs | 307 ++++ vendor/gix-transport/src/client/git/async_io.rs | 151 ++ vendor/gix-transport/src/client/git/blocking_io.rs | 199 +++ vendor/gix-transport/src/client/git/mod.rs | 177 +++ vendor/gix-transport/src/client/mod.rs | 36 + vendor/gix-transport/src/client/non_io_types.rs | 159 +++ vendor/gix-transport/src/client/traits.rs | 115 ++ vendor/gix-transport/src/lib.rs | 99 ++ 32 files changed, 6091 insertions(+) create mode 100644 vendor/gix-transport/.cargo-checksum.json create mode 100644 vendor/gix-transport/CHANGELOG.md create mode 100644 vendor/gix-transport/Cargo.toml create mode 100644 vendor/gix-transport/src/client/async_io/bufread_ext.rs create mode 100644 vendor/gix-transport/src/client/async_io/connect.rs create mode 100644 vendor/gix-transport/src/client/async_io/mod.rs create mode 100644 vendor/gix-transport/src/client/async_io/request.rs create mode 100644 vendor/gix-transport/src/client/async_io/traits.rs create mode 100644 vendor/gix-transport/src/client/blocking_io/bufread_ext.rs create mode 100644 vendor/gix-transport/src/client/blocking_io/connect.rs create mode 100644 vendor/gix-transport/src/client/blocking_io/file.rs create mode 100644 vendor/gix-transport/src/client/blocking_io/http/curl/mod.rs create mode 100644 vendor/gix-transport/src/client/blocking_io/http/curl/remote.rs create mode 100644 vendor/gix-transport/src/client/blocking_io/http/mod.rs create mode 100644 vendor/gix-transport/src/client/blocking_io/http/redirect.rs create mode 100644 vendor/gix-transport/src/client/blocking_io/http/reqwest/mod.rs create mode 100644 vendor/gix-transport/src/client/blocking_io/http/reqwest/remote.rs create mode 100644 vendor/gix-transport/src/client/blocking_io/http/traits.rs create mode 100644 vendor/gix-transport/src/client/blocking_io/mod.rs create mode 100644 vendor/gix-transport/src/client/blocking_io/request.rs create mode 100644 vendor/gix-transport/src/client/blocking_io/ssh/mod.rs create mode 100644 vendor/gix-transport/src/client/blocking_io/ssh/program_kind.rs create mode 100644 vendor/gix-transport/src/client/blocking_io/ssh/tests.rs create mode 100644 vendor/gix-transport/src/client/blocking_io/traits.rs create mode 100644 vendor/gix-transport/src/client/capabilities.rs create mode 100644 vendor/gix-transport/src/client/git/async_io.rs create mode 100644 vendor/gix-transport/src/client/git/blocking_io.rs create mode 100644 vendor/gix-transport/src/client/git/mod.rs create mode 100644 vendor/gix-transport/src/client/mod.rs create mode 100644 vendor/gix-transport/src/client/non_io_types.rs create mode 100644 vendor/gix-transport/src/client/traits.rs create mode 100644 vendor/gix-transport/src/lib.rs (limited to 'vendor/gix-transport') 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 + + + + - 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 + + + +
view details + + * **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)) +
+ +## 0.26.0 (2023-03-01) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 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 + + + +
view details + + * **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)) +
+ +## 0.25.6 (2023-02-20) + +### Bug Fixes + + - 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 + + + + - 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 + + + +
view details + + * **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)) +
+ +## 0.25.5 (2023-02-17) + +### Bug Fixes + + - 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. + - 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 + + + + - 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 + + + +
view details + + * **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)) +
+ +## 0.25.4 (2023-02-17) + + + + + + +### Bug Fixes (BREAKING) + + - `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. + - `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) + + - 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. + - 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. + - 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`. + - 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. + - 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. + - add `fetch::Transport::configure` to generically configure any transport. + - use `git-credentials` in `git-protocol` + +### Changed (BREAKING) + + - `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. + - `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). + - `client::TransportV2Ext::invoke()` supports owned `capabilities`. + That way it's easier to pass custom agent strings when invoking. + - `client::http::connect()` returns `Transport` directly. + It' can't fail, so no need to return `Result<_, Infallible>`. + - upgrade `bstr` to `1.0.1` + - `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. + - `client::connect()` now takes a `&BStr` as URL + - use `thiserror` instead of `quickerror` + - 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 + + - 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. + - make capabilities parsing public + +### New Features + + + + + + + + + + + + + + + + - add and implement various new http options for the `curl` backend. + - `schannel_check_revoke` as `curl`-backend specific configuration. + +### Chore + + - uniformize deny attributes + - remove default link to cargo doc everywhere + +### Changed + + - `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 + + - fix typos + +### Bug Fixes + + + + + + + + + + + + + + + + - make clear in docs that the writer needs to be dropped for good measure. + Otherwise, some transports might deadlock. + - 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) + - Place port for PuTTY and derivates in separate argument + All three PuTTY clients require the port to be separated from the "-P" + argument + - `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. + - 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. + - improve error message for when an invoked transport program can't be found. + - assure processing thread is up before continuing. + That way one may hope that we never leave stderr output unprocessed. + - port selections for SSH urls are now respected for protocol V1 as well. + - 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`. + - fixes SSH clone from scp-like/relatives URLs + - Removes git-upload-pack extra parameters (rejected by both github and gitlab) + +### Commit Statistics + + + + - 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 + + + +[Clippy](https://github.com/rust-lang/rust-clippy) helped 26 times to make code idiomatic. + +### Commit Details + + + +
view details + + * **[#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` 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- 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 ' ([`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)) +
+ +## 0.25.3 (2023-01-10) + +A maintenance release without user-facing changes. + +## 0.25.2 (2023-01-09) + +### Bug Fixes + + - `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 + + - 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 + + - improve error message for when an invoked transport program can't be found. + - assure processing thread is up before continuing. + That way one may hope that we never leave stderr output unprocessed. + - port selections for SSH urls are now respected for protocol V1 as well. + - 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) + + - 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 + + - add and implement various new http options for the `curl` backend. + - `schannel_check_revoke` as `curl`-backend specific configuration. + +### Bug Fixes + + - 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 + + - 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. + - `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. + - `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. + - `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 + + - http transports can now reuse a connection. + This makes connections more efficient generally and `cargo` relies + on that behaviour in their tests as well. + - 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. + - ssh connection remove '=' in port argument + - make it possible to parse handshakes without newlines in packetlines #(639) + - correctly display what's actual and expected when failing to parse capabilities. + - improve compile-time errors if mutually exclusive http-client features are set. + - 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) + + - 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`. + - 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. + - 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) + + - `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 + + - Support for proxy authentication in http configuration. + +### New Features (BREAKING) + + - 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) + + - `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 + + - `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 + + - `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) + + - `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. + - `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). + - `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 + + - Allow `client::connect()` to function with `http-client-reqwest` enabled. + - `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) + + + +### New Features + + - `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. + - 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. + - 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 + + - 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. + - 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. + - compare 'Content-Type' header case-insensitively, as required by the http spec. + +### Other + + - 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) + + - `client::http::connect()` returns `Transport` directly. + It' can't fail, so no need to return `Result<_, Infallible>`. + +### New Features (BREAKING) + + - add `fetch::Transport::configure` to generically configure any transport. + +## 0.20.0 (2022-09-20) + +### Changed (BREAKING) + + - upgrade `bstr` to `1.0.1` + +## 0.19.3 (2022-08-28) + +Maintenance release without user-facing changes. + +## 0.19.2 (2022-08-24) + + + + +### Chore + + - uniformize deny attributes + - remove default link to cargo doc everywhere + +### New Features + + - use docsrs feature in code to show what is feature-gated automatically on docs.rs + - pass --cfg docsrs when compiling for https://docs.rs + +### Bug Fixes + + - Make async `conenct()` signature compatible with the blocking implementation. + +## 0.19.1 (2022-08-17) + +A maintenance release without user facing changes. + +### Changed (BREAKING) + + - `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. + - `client::connect()` now takes a `&BStr` as URL + - use `thiserror` instead of `quickerror` + - 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 + + - `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) + + - use `gix-credentials` in `gix-protocol` + +## 0.16.0 (2022-04-03) + +### New Features + + - 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) + + + +### Other + + - 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 "] +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; + +/// 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, 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); + /// 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>>; + /// 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; +} + +#[async_trait(?Send)] +impl<'a, T: ReadlineBufRead + ?Sized + 'a + Unpin> ReadlineBufRead for Box { + async fn readline(&mut self) -> Option, gix_packetline::decode::Error>>> { + self.deref_mut().readline().await + } +} + +#[async_trait(?Send)] +impl<'a, T: ExtendedBufRead + ?Sized + 'a + Unpin> ExtendedBufRead for Box { + fn set_progress_handler(&mut self, handle_progress: Option) { + self.deref_mut().set_progress_handler(handle_progress) + } + + async fn peek_data_line(&mut self) -> Option>> { + self.deref_mut().peek_data_line().await + } + + fn reset(&mut self, version: Protocol) { + self.deref_mut().reset(version) + } + + fn stopped_at(&self) -> Option { + self.deref().stopped_at() + } +} + +#[async_trait(?Send)] +impl ReadlineBufRead for gix_packetline::read::WithSidebands<'_, T, for<'b> fn(bool, &'b [u8])> { + async fn readline(&mut self) -> Option, 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, 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) { + self.set_progress_handler(handle_progress) + } + async fn peek_data_line(&mut self) -> Option>> { + 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 { + 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: Url, + options: super::Options, + ) -> Result, Error> + where + Url: TryInto, + gix_url::parse::Error: From, + { + 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)?, + ) + } + 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>, + reader: Box, + } +} +impl<'a> futures_io::AsyncWrite for RequestWriter<'a> { + fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { + self.project().writer.poll_write(cx, buf) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().writer.poll_flush(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + 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( + writer: W, + reader: Box, + write_mode: WriteMode, + on_into_read: MessageKind, + ) -> Self { + let mut writer = gix_packetline::Writer::new(Box::new(writer) as Box); + 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> { + 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, Box) { + (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>, +} + +/// 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, Error>; +} + +// Would be nice if the box implementation could auto-forward to all implemented traits. +#[async_trait(?Send)] +impl Transport for Box { + async fn handshake<'a>( + &mut self, + service: Service, + extra_parameters: &'a [(&'a str, Option<&'a str>)], + ) -> Result, 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 Transport for &mut T { + async fn handshake<'a>( + &mut self, + service: Service, + extra_parameters: &'a [(&'a str, Option<&'a str>)], + ) -> Result, 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>)> + 'a, + arguments: Option + 'a>, + ) -> Result, Error>; +} + +#[async_trait(?Send)] +impl TransportV2Ext for T { + async fn invoke<'a>( + &mut self, + command: &str, + capabilities: impl Iterator>)> + 'a, + arguments: Option + 'a>, + ) -> Result, 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; + +/// 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, 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); + /// 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>>; + /// 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; +} + +impl<'a, T: ReadlineBufRead + ?Sized + 'a> ReadlineBufRead for Box { + fn readline(&mut self) -> Option, gix_packetline::decode::Error>>> { + ReadlineBufRead::readline(self.deref_mut()) + } +} + +impl<'a, T: ExtendedBufRead + ?Sized + 'a> ExtendedBufRead for Box { + fn set_progress_handler(&mut self, handle_progress: Option) { + self.deref_mut().set_progress_handler(handle_progress) + } + + fn peek_data_line(&mut self) -> Option>> { + self.deref_mut().peek_data_line() + } + + fn reset(&mut self, version: Protocol) { + self.deref_mut().reset(version) + } + + fn stopped_at(&self) -> Option { + self.deref().stopped_at() + } +} + +impl ReadlineBufRead for gix_packetline::read::WithSidebands<'_, T, fn(bool, &[u8])> { + fn readline(&mut self) -> Option, 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, 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) { + self.set_progress_handler(handle_progress) + } + fn peek_data_line(&mut self) -> Option>> { + 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 { + 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: Url, options: super::Options) -> Result, Error> + where + Url: TryInto, + gix_url::parse::Error: From, + { + 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)?, + ) + } + 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)? + }), + 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)? + }) + } + #[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, process::ChildStdin>>, + child: Option, +} + +impl SpawnProcessOnDemand { + pub(crate) fn new_ssh( + url: gix_url::Url, + program: impl Into, + 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, 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> { + Ok(()) + } +} + +struct ReadStdoutFailOnError { + recv: std::sync::mpsc::Receiver, + 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) -> std::io::Result { + 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 { + 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, 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 = 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, + desired_version: Protocol, +) -> Result { + 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, +} + +/// 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, + res: Receiver, + handle: Option>>, + 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>, + upload_body_kind: Option, + ) -> Result, 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>, + ) -> Result, 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>, + body: PostBodyDataKind, + ) -> Result, http::Error> { + self.make_request(url, base_url, headers, Some(body)) + } + + fn configure( + &mut self, + config: &dyn std::any::Any, + ) -> Result<(), Box> { + if let Some(config) = config.downcast_ref::() { + 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>), +} + +#[derive(Default)] +struct Handler { + send_header: Option, + send_data: Option, + receive_body: Option, + 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> { + let code = data + .split(|b| *b == b' ') + .nth(1) + .ok_or("Expected HTTP/ STATUS")?; + let code = std::str::from_utf8(code)?; + code.parse().map_err(Into::into) + } + fn parse_status(data: &[u8], follow: FollowRedirects) -> Option<(usize, Box)> { + 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 { + 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 { + 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, + 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>, + SyncSender, + Receiver, +) { + 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::; + + 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::() { + 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::::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 for http::Error { + fn from(err: Error) -> Self { + http::Error::Detail { + description: err.to_string(), + } + } +} + +impl From 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, + /// 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, + /// The comma-separated list of hosts to not send through the `proxy`, or `*` to entirely disable all proxying. + pub no_proxy: Option, + /// 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>, + )>, + /// 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, + /// 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, + /// 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, + /// The SSL version or version range to use, or `None` to let the TLS backend determine which versions are acceptable. + pub ssl_version: Option, + /// The HTTP version to enforce. If unset, it is implementation defined. + pub http_version: Option, + /// Backend specific options, if available. + pub backend: Option>>, +} + +/// 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 { + url: String, + user_agent_header: &'static str, + desired_version: Protocol, + actual_version: Protocol, + http: H, + service: Option, + line_provider: Option>, + identity: Option, +} + +impl Transport { + /// 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 { + /// 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 Transport { + fn check_content_type(service: Service, kind: &str, headers: ::Headers) -> Result<(), client::Error> { + let wanted_content_type = format!("application/x-{}-{}", service.as_str(), kind); + if !headers.lines().collect::, _>>()?.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>) -> 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 client::TransportWithoutIO for Transport { + 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, 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:: { + 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> { + self.http.configure(config) + } +} + +impl client::Transport for Transport { + fn handshake<'a>( + &mut self, + service: Service, + extra_parameters: &'a [(&'a str, Option<&'a str>)], + ) -> Result, 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::>::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::>() + .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))?; + >::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 { + service: Service, + headers: Option, + body: B, +} + +impl HeadersThenBody { + fn handle_headers(&mut self) -> std::io::Result<()> { + if let Some(headers) = self.headers.take() { + >::check_content_type(self.service, "result", headers) + .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))? + } + Ok(()) + } +} + +impl Read for HeadersThenBody { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.handle_headers()?; + self.body.read(buf) + } +} + +impl BufRead for HeadersThenBody { + 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 ReadlineBufRead for HeadersThenBody { + fn readline(&mut self) -> Option, gix_packetline::decode::Error>>> { + if let Err(err) = self.handle_headers() { + return Some(Err(err)); + } + self.body.readline() + } +} + +impl ExtendedBufRead for HeadersThenBody { + fn set_progress_handler(&mut self, handle_progress: Option) { + self.body.set_progress_handler(handle_progress) + } + + fn peek_data_line(&mut self) -> Option>> { + 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 { + 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(http: H, url: &str, desired_version: Protocol) -> Transport { + 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 { + 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 { + 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>>, + /// A channel to send requests (work) to the worker thread. + request: std::sync::mpsc::SyncSender, + /// A channel to receive the result of the prior request. + response: std::sync::mpsc::Receiver, + /// 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> + + 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>, +} + +/// +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), +} + +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::::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::() { + 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>, + upload_body_kind: Option, + ) -> Result, 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>, + ) -> Result, 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>, + post_body_kind: PostBodyDataKind, + ) -> Result, http::Error> { + self.make_request(url, base_url, headers, Some(post_body_kind)) + } + + fn configure(&mut self, config: &dyn Any) -> Result<(), Box> { + if let Some(config) = config.downcast_ref::() { + self.config = config.clone(); + } + Ok(()) + } +} + +pub(crate) struct Request { + pub url: String, + pub headers: reqwest::header::HeaderMap, + pub upload_body_kind: Option, + 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, + }, + #[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::() { + return err.is_spurious(); + }; + #[cfg(feature = "http-client-reqwest")] + if let Some(err) = source.downcast_ref::() { + return err.is_spurious(); + }; + false + } + _ => false, + } + } +} + +/// The return value of [Http::get()]. +pub struct GetResponse { + /// The response headers. + pub headers: H, + /// The response body. + pub body: B, +} + +/// The return value of [Http::post()]. +pub struct PostResponse { + /// 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: `. + 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 for PostBodyDataKind { + fn from(m: WriteMode) -> Self { + match m { + WriteMode::Binary => PostBodyDataKind::Unbounded, + WriteMode::OneLfTerminatedLinePerWriteCall => PostBodyDataKind::BoundedAndFitsIntoMemory, + } + } +} + +impl From> for GetResponse { + fn from(v: PostResponse) -> 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>, + ) -> Result, 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>, + body: PostBodyDataKind, + ) -> Result, 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>; +} 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>, + reader: Box, +} + +impl<'a> io::Write for RequestWriter<'a> { + fn write(&mut self, buf: &[u8]) -> io::Result { + 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( + writer: W, + reader: Box, + write_mode: WriteMode, + on_into_read: MessageKind, + ) -> Self { + let mut writer = gix_packetline::Writer::new(Box::new(writer) as Box); + 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> { + 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, Box) { + (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, + /// 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, + } + + 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 { + 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 { + 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 { + 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::>().join(" ") + } + fn try_call( + kind: ProgramKind, + url: &str, + version: Protocol, + ) -> std::result::Result { + 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>, +} + +/// 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, Error>; +} + +// Would be nice if the box implementation could auto-forward to all implemented traits. +impl Transport for Box { + fn handshake<'a>( + &mut self, + service: Service, + extra_parameters: &'a [(&'a str, Option<&'a str>)], + ) -> Result, Error> { + self.deref_mut().handshake(service, extra_parameters) + } +} + +impl Transport for &mut T { + fn handshake<'a>( + &mut self, + service: Service, + extra_parameters: &'a [(&'a str, Option<&'a str>)], + ) -> Result, 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>)> + 'a, + arguments: Option>, + ) -> Result, Error>; +} + +impl TransportV2Ext for T { + fn invoke<'a>( + &mut self, + command: &str, + capabilities: impl Iterator>)> + 'a, + arguments: Option>, + ) -> Result, 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> { + 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 { + 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 { + 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> { + self.iter().find(|c| c.name() == name.as_bytes().as_bstr()) + } + + /// Returns an iterator over all capabilities. + pub fn iter(&self) -> impl Iterator> { + 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 { + 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>, + /// 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( + rd: &mut gix_packetline::StreamingPeekableIter, + ) -> Result, 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>, + /// 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( + rd: &mut gix_packetline::StreamingPeekableIter, + ) -> Result, 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 client::TransportWithoutIO for git::Connection +where + R: AsyncRead + Unpin, + W: AsyncWrite + Unpin, +{ + fn request( + &mut self, + write_mode: client::WriteMode, + on_into_read: client::MessageKind, + ) -> Result, 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> { + Ok(()) + } +} + +#[async_trait(?Send)] +impl client::Transport for git::Connection +where + R: AsyncRead + Unpin, + W: AsyncWrite + Unpin, +{ + async fn handshake<'a>( + &mut self, + service: Service, + extra_parameters: &'a [(&'a str, Option<&'a str>)], + ) -> Result, 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 git::Connection +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, + virtual_host: Option<(impl Into, Option)>, + 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 { + /// 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, + path: bstr::BString, + desired_version: crate::Protocol, + ) -> Result, 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 client::TransportWithoutIO for git::Connection +where + R: std::io::Read, + W: std::io::Write, +{ + fn request( + &mut self, + write_mode: client::WriteMode, + on_into_read: client::MessageKind, + ) -> Result, 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> { + Ok(()) + } +} + +impl client::Transport for git::Connection +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, 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 git::Connection +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, + virtual_host: Option<(impl Into, Option)>, + 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, + ) -> 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 [: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), 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, + ) -> Result, 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 { + pub(in crate::client) writer: W, + pub(in crate::client) line_provider: gix_packetline::StreamingPeekableIter, + pub(in crate::client) path: BString, + pub(in crate::client) virtual_host: Option<(String, Option)>, + pub(in crate::client) desired_version: Protocol, + custom_url: Option, + pub(in crate::client) mode: ConnectMode, +} + +impl Connection { + /// 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) -> 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)>, + 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), + #[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::() { + return err.is_spurious(); + }; + if let Some(err) = err.downcast_ref::() { + 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, 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>; +} + +// Would be nice if the box implementation could auto-forward to all implemented traits. +impl TransportWithoutIO for Box { + 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, 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> { + self.deref_mut().configure(config) + } +} + +impl 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, 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> { + 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"); -- cgit v1.2.3