diff options
Diffstat (limited to 'vendor/gix-pathspec')
27 files changed, 3076 insertions, 0 deletions
diff --git a/vendor/gix-pathspec/.cargo-checksum.json b/vendor/gix-pathspec/.cargo-checksum.json new file mode 100644 index 000000000..776151660 --- /dev/null +++ b/vendor/gix-pathspec/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CHANGELOG.md":"e260578f0c5595ab2d17eddd0309da451ee37752b8ec31c09193670c5f6dfe6e","Cargo.toml":"bdb90604393782c6ea835edad435a65ccff130a667fed4471b7bc6edf9bca307","LICENSE-APACHE":"cb4780590812826851ba250f90bed0ed19506ec98f6865a0e2e20bbf62391ff9","LICENSE-MIT":"49df47913ab2beafe8dc45607877ae64198bf0eee64aaad3e82ed9e4d27424e8","README.md":"ec95cf838fbde2a636e2b1de88ddde926ddbc59b496590d7a8edbd283d87875f","src/defaults.rs":"ff76c0704678059c26924f729c36f40c501d21cb0afa793359a73e67a0f2e736","src/lib.rs":"74f77299f99c32714396a2815e014474af44414941786a82cc1a3be930d47a9d","src/parse.rs":"c279f433b8585447c2f6e1d02329f8a2eb86694333b2dd6af45fedbb1faef724","src/pattern.rs":"629c3225eb83eb9e3606eeda558f8e66c12defacc99f95f00e177308322bb576","src/search/init.rs":"a26f6335f8feb7c8feac0390c07da0b64499d5a7a7d3f263149ddfe678025356","src/search/matching.rs":"e9529ca5c88762f19c890a5f210339d4b1febe8fc6633eb6f1b2d69367855d93","src/search/mod.rs":"31a3b65392c3791cb9b66f8a799275ba5d4b13a0e82bd1401bbb114cd3357ef0","tests/defaults.rs":"fb9e0c78258e96f89138e77f1d9fbb009de2ff490a3c2ecd0637e826a105dd69","tests/fixtures/generated-archives/match_baseline.tar.xz":"0ba46801dfab65bcc77c8ce0dc9144d77fd142be847f3340af6ae8fcba44c82d","tests/fixtures/generated-archives/match_baseline_dirs.tar.xz":"742241f4ea290fc9088fd5e32423859929bc4b39dccd31d205c67fe3ac9128c0","tests/fixtures/generated-archives/match_baseline_files.tar.xz":"b0c722ab08de2ab63b9f222747cf898f5b6bb6b76ec55ccd176bab3b854ef7bb","tests/fixtures/generated-archives/parse_baseline.tar.xz":"123da727b33802a3a2dc80e948b07cbf133c2d66129693f4896cba2f6b92b02f","tests/fixtures/match_baseline_dirs.sh":"202f2d67ed9107507e4c648b2ad78934ea2a3f6b6865f0a8367fe7aa6aca7614","tests/fixtures/match_baseline_files.sh":"3fa45af77c32b570c419b9746539ee9f074e13113437f5a29931126b155cb1ff","tests/fixtures/parse_baseline.sh":"55415e80ce66bce46bf342036b7fd6e8a3a9094cb058cade13af325347e8bb73","tests/normalize/mod.rs":"6d39e550b255da354b1566f01d21f270b5235bbae760448ceec734cef3930569","tests/parse/invalid.rs":"7bc793a7718a46fd08c2de87d971cdc044788d9df8c6499ca4e3022caed6d955","tests/parse/mod.rs":"e712b38cc012ff5775a9ff60c1a6f70a7c2411dad42ae5e496068db71b1565d4","tests/parse/valid.rs":"ab1f68baaeaef7d6320304117e39531e78d36ec038f78a74d49f82ac66675950","tests/pathspec.rs":"63ed83deae59a91ba60b36ffccebd5ea01229eb20a10f654595441f511dea14f","tests/search/mod.rs":"b701d3431edb87032bc85b0375cd47050210af7ab438f598c71733ad41558d97"},"package":"c3e26c9b47c51be73f98d38c84494bd5fb99334c5d6fda14ef5d036d50a9e5fd"}
\ No newline at end of file diff --git a/vendor/gix-pathspec/CHANGELOG.md b/vendor/gix-pathspec/CHANGELOG.md new file mode 100644 index 000000000..d742565d3 --- /dev/null +++ b/vendor/gix-pathspec/CHANGELOG.md @@ -0,0 +1,328 @@ +# 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.3.0 (2023-09-24) + +<csr-id-832b34570bde97499e75a174bc818113092b3145/> + +### Other + + - <csr-id-832b34570bde97499e75a174bc818113092b3145/> improve documentation of `common_prefix()` and `pattern_matching_relative_path()`. + +### Commit Statistics + +<csr-read-only-do-not-edit/> + + - 3 commits contributed to the release over the course of 1 calendar day. + - 16 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + +<csr-read-only-do-not-edit/> + +<details><summary>view details</summary> + + * **Uncategorized** + - Prepare changelogs prior to release ([`8a60d5b`](https://github.com/Byron/gitoxide/commit/8a60d5b80877c213c3b646d3061e8a33e0e433ec)) + - Merge branch 'reset' ([`54a8495`](https://github.com/Byron/gitoxide/commit/54a849545140f7f1c0c7564c418071c0a76a34e7)) + - Improve documentation of `common_prefix()` and `pattern_matching_relative_path()`. ([`832b345`](https://github.com/Byron/gitoxide/commit/832b34570bde97499e75a174bc818113092b3145)) +</details> + +## 0.2.0 (2023-09-08) + +### Bug Fixes (BREAKING) + + - <csr-id-072ee32f693a31161cd6a843da6582d13efbb20b/> use `dyn` trait where possible. + This reduces compile time due to avoiding duplication. + +### Commit Statistics + +<csr-read-only-do-not-edit/> + + - 5 commits contributed to the release over the course of 17 calendar days. + - 17 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + +<csr-read-only-do-not-edit/> + +<details><summary>view details</summary> + + * **Uncategorized** + - Release gix-date v0.8.0, gix-hash v0.13.0, gix-features v0.34.0, gix-actor v0.26.0, gix-object v0.36.0, gix-path v0.10.0, gix-glob v0.12.0, gix-attributes v0.18.0, gix-packetline-blocking v0.16.6, gix-filter v0.4.0, gix-fs v0.6.0, gix-commitgraph v0.20.0, gix-hashtable v0.4.0, gix-revwalk v0.7.0, gix-traverse v0.32.0, gix-worktree-stream v0.4.0, gix-archive v0.4.0, gix-config-value v0.14.0, gix-tempfile v9.0.0, gix-lock v9.0.0, gix-ref v0.36.0, gix-sec v0.10.0, gix-config v0.29.0, gix-prompt v0.7.0, gix-url v0.23.0, gix-credentials v0.19.0, gix-diff v0.35.0, gix-discover v0.24.0, gix-ignore v0.7.0, gix-index v0.24.0, gix-macros v0.1.0, gix-mailmap v0.18.0, gix-negotiate v0.7.0, gix-pack v0.42.0, gix-odb v0.52.0, gix-pathspec v0.2.0, gix-packetline v0.16.6, gix-transport v0.36.0, gix-protocol v0.39.0, gix-revision v0.21.0, gix-refspec v0.17.0, gix-submodule v0.3.0, gix-worktree v0.25.0, gix-worktree-state v0.2.0, gix v0.53.0, safety bump 39 crates ([`8bd0456`](https://github.com/Byron/gitoxide/commit/8bd045676bb2cdc02624ab93e73ff8518064ca38)) + - Prepare changelogs for release ([`375db06`](https://github.com/Byron/gitoxide/commit/375db06a8442378c3f7a922fae38e2a6694d9d04)) + - Merge branch `dyn`ification ([`f658fcc`](https://github.com/Byron/gitoxide/commit/f658fcc52dc2200ae34ca53dc10be97fb9012057)) + - Use `dyn` trait where possible. ([`072ee32`](https://github.com/Byron/gitoxide/commit/072ee32f693a31161cd6a843da6582d13efbb20b)) + - Merge branch 'gix-submodule' ([`363ee77`](https://github.com/Byron/gitoxide/commit/363ee77400805f473c9ad66eadad9214e7ab66f4)) +</details> + +## 0.1.0 (2023-08-22) + +<csr-id-f7f136dbe4f86e7dee1d54835c420ec07c96cd78/> +<csr-id-533e887e80c5f7ede8392884562e1c5ba56fb9a8/> +<csr-id-229bd4899213f749a7cc124aa2b82a1368fba40f/> +<csr-id-bcad5c22049d56a25ef69d6c7a3344e78f9a1d4d/> + +### Chore + + - <csr-id-f7f136dbe4f86e7dee1d54835c420ec07c96cd78/> uniformize deny attributes + - <csr-id-533e887e80c5f7ede8392884562e1c5ba56fb9a8/> remove default link to cargo doc everywhere + +### New Features + + - <csr-id-07a3e93d22462ed0221ee90bda17559425305528/> export `gix-attributes` as `attributes` to make `gix-glob` and `gix-attribute` types available. + - <csr-id-49db3ac81db18fbc492d14161c09084112ff62f3/> match pathspecs just like `git` does. + This is important for selecting files on disk + - <csr-id-b3de72e014b33bdf1ebc39cd7e3e5db990e5b7f3/> Add `Pattern::is_null()` to be able to check for a special kind of spec. + That spec is indicated with `:`. + +### Chore + + - <csr-id-229bd4899213f749a7cc124aa2b82a1368fba40f/> don't call crate 'WIP' in manifest anymore. + - <csr-id-bcad5c22049d56a25ef69d6c7a3344e78f9a1d4d/> Add `clippy::redundant-closure-for-method-calls` lint + +### Bug Fixes + + - <csr-id-e14dc7d475373d2c266e84ff8f1826c68a34ab92/> note that crates have been renamed from `git-*` to `gix-*`. + This also means that the `git-*` prefixed crates of the `gitoxide` project + are effectively unmaintained. + Use the crates with the `gix-*` prefix instead. + + If you were using `git-repository`, then `gix` is its substitute. + - <csr-id-a2f1879f85df75146baf32336cf5a7b19dae032e/> certain combinations of exclude specs could cause panics. + - <csr-id-135d317065aae87af302beb6c26bb6ca8e30b6aa/> compatibility with `bstr` v1.3, use `*.as_bytes()` instead of `.as_ref()`. + `as_ref()` relies on a known target type which isn't always present. However, once + there is only one implementation, that's no problem, but when that changes compilation + fails due to ambiguity. + +### Changed (BREAKING) + + - <csr-id-99905bacace8aed42b16d43f0f04cae996cb971c/> upgrade `bstr` to `1.0.1` + +### New Features (BREAKING) + + - <csr-id-3d8fa8fef9800b1576beab8a5bc39b821157a5ed/> upgrade edition to 2021 in most crates. + MSRV for this is 1.56, and we are now at 1.60 so should be compatible. + This isn't more than a patch release as it should break nobody + who is adhering to the MSRV, but let's be careful and mark it + breaking. + + Note that `git-features` and `git-pack` are still on edition 2018 + as they make use of a workaround to support (safe) mutable access + to non-overlapping entries in a slice which doesn't work anymore + in edition 2021. + - <csr-id-f1421d31ab9e045f6b6599d21eb1a0723f90a242/> `parse()` now supports the configuration of defaults. + This allows the caller to control what happens if certain configuration + flags of pathsepcs aren't set. + + These are typically provided by pathspec configuration environment variables. + +### Commit Statistics + +<csr-read-only-do-not-edit/> + + - 169 commits contributed to the release over the course of 523 calendar days. + - 12 commits were understood as [conventional](https://www.conventionalcommits.org). + - 5 unique issues were worked on: [#301](https://github.com/Byron/gitoxide/issues/301), [#415](https://github.com/Byron/gitoxide/issues/415), [#427](https://github.com/Byron/gitoxide/issues/427), [#450](https://github.com/Byron/gitoxide/issues/450), [#691](https://github.com/Byron/gitoxide/issues/691) + +### Thanks Clippy + +<csr-read-only-do-not-edit/> + +[Clippy](https://github.com/rust-lang/rust-clippy) helped 4 times to make code idiomatic. + +### Commit Details + +<csr-read-only-do-not-edit/> + +<details><summary>view details</summary> + + * **[#301](https://github.com/Byron/gitoxide/issues/301)** + - Add future crate for good measure ([`625eb1d`](https://github.com/Byron/gitoxide/commit/625eb1d7d266036c1f30caab7bd3897af9fdbef7)) + * **[#415](https://github.com/Byron/gitoxide/issues/415)** + - Changed quickerror to thiserror ([`49fcab7`](https://github.com/Byron/gitoxide/commit/49fcab749ea3260f3333976567fcc4ab8a072fe3)) + - Added alternative parsing module ([`eb2dec0`](https://github.com/Byron/gitoxide/commit/eb2dec0777bfe894f4d02bdc03a3e507302b41b4)) + - Attribute parsing functional ([`4b2ed7e`](https://github.com/Byron/gitoxide/commit/4b2ed7e0f033fd01a7364fc41057e113adf2fbdc)) + - Updated documentation ([`8b9570f`](https://github.com/Byron/gitoxide/commit/8b9570fdcdb426cbde7edb9fcca4b7340944550e)) + - Attribute parsing WIP ([`7c84fb8`](https://github.com/Byron/gitoxide/commit/7c84fb81506cc41fbf68575583129eafbd89139d)) + - Added some documentation ([`c04d4be`](https://github.com/Byron/gitoxide/commit/c04d4be29c7a0cf72500d30432215513f6066338)) + - Tests now check if pathspec is valid in git ([`334659e`](https://github.com/Byron/gitoxide/commit/334659e34b1f99f3bc662fa599dcd8f0d94ad206)) + - Remove WhitespaceError ([`4d20cd9`](https://github.com/Byron/gitoxide/commit/4d20cd91be403d48fd5443202048f4a5bb867a62)) + - Error handling: parser can return a result now ([`31aba11`](https://github.com/Byron/gitoxide/commit/31aba11c953b5b7dd70f14ee904026d12db69d10)) + - Pathspec parser is functional ([`7d95f16`](https://github.com/Byron/gitoxide/commit/7d95f162a3edb7b2714dfadb9b5cf8311f3da061)) + * **[#427](https://github.com/Byron/gitoxide/issues/427)** + - Make fmt ([`4b320e7`](https://github.com/Byron/gitoxide/commit/4b320e773368ac5e8c38dd8a779ef3d6d2d024ec)) + * **[#450](https://github.com/Byron/gitoxide/issues/450)** + - Upgrade `bstr` to `1.0.1` ([`99905ba`](https://github.com/Byron/gitoxide/commit/99905bacace8aed42b16d43f0f04cae996cb971c)) + * **[#691](https://github.com/Byron/gitoxide/issues/691)** + - Set `rust-version` to 1.64 ([`55066ce`](https://github.com/Byron/gitoxide/commit/55066ce5fd71209abb5d84da2998b903504584bb)) + * **Uncategorized** + - Release gix-url v0.22.0, gix-credentials v0.18.0, gix-diff v0.34.0, gix-discover v0.23.0, gix-ignore v0.6.0, gix-bitmap v0.2.7, gix-index v0.22.0, gix-mailmap v0.17.0, gix-negotiate v0.6.0, gix-pack v0.41.0, gix-odb v0.51.0, gix-pathspec v0.1.0, gix-packetline v0.16.5, gix-transport v0.35.0, gix-protocol v0.38.0, gix-revision v0.20.0, gix-refspec v0.16.0, gix-submodule v0.2.0, gix-worktree v0.24.0, gix-worktree-state v0.1.0, gix v0.52.0, gitoxide-core v0.31.0, gitoxide v0.29.0 ([`6c62e74`](https://github.com/Byron/gitoxide/commit/6c62e748240ac0980fc23fdf30f8477dea8b9bc3)) + - Release gix-date v0.7.3, gix-hash v0.12.0, gix-features v0.33.0, gix-actor v0.25.0, gix-object v0.35.0, gix-path v0.9.0, gix-glob v0.11.0, gix-quote v0.4.7, gix-attributes v0.17.0, gix-command v0.2.9, gix-packetline-blocking v0.16.5, gix-filter v0.3.0, gix-fs v0.5.0, gix-commitgraph v0.19.0, gix-hashtable v0.3.0, gix-revwalk v0.6.0, gix-traverse v0.31.0, gix-worktree-stream v0.3.0, gix-archive v0.3.0, gix-config-value v0.13.0, gix-tempfile v8.0.0, gix-lock v8.0.0, gix-ref v0.35.0, gix-sec v0.9.0, gix-config v0.28.0, gix-prompt v0.6.0, gix-url v0.22.0, gix-credentials v0.18.0, gix-diff v0.34.0, gix-discover v0.23.0, gix-ignore v0.6.0, gix-bitmap v0.2.7, gix-index v0.22.0, gix-mailmap v0.17.0, gix-negotiate v0.6.0, gix-pack v0.41.0, gix-odb v0.51.0, gix-pathspec v0.1.0, gix-packetline v0.16.5, gix-transport v0.35.0, gix-protocol v0.38.0, gix-revision v0.20.0, gix-refspec v0.16.0, gix-submodule v0.2.0, gix-worktree v0.24.0, gix-worktree-state v0.1.0, gix v0.52.0, gitoxide-core v0.31.0, gitoxide v0.29.0, safety bump 41 crates ([`30b2761`](https://github.com/Byron/gitoxide/commit/30b27615047692d3ced1b2d9c2ac15a80f79fbee)) + - Update changelogs prior to release ([`f23ea88`](https://github.com/Byron/gitoxide/commit/f23ea8828f2d9ba7559973daca388c9591bcc5fc)) + - Just fmt ([`0d258f4`](https://github.com/Byron/gitoxide/commit/0d258f40afcd848509e2b0c7c264e9f346ed1726)) + - Don't call crate 'WIP' in manifest anymore. ([`229bd48`](https://github.com/Byron/gitoxide/commit/229bd4899213f749a7cc124aa2b82a1368fba40f)) + - Merge branch 'submodule-active' ([`a3afaa4`](https://github.com/Byron/gitoxide/commit/a3afaa42741616a0f1abeef9b54557e7c2b800cb)) + - Export `gix-attributes` as `attributes` to make `gix-glob` and `gix-attribute` types available. ([`07a3e93`](https://github.com/Byron/gitoxide/commit/07a3e93d22462ed0221ee90bda17559425305528)) + - Add `Defaults::from_environment()` to be able to respect git environment settings. ([`fb9d4db`](https://github.com/Byron/gitoxide/commit/fb9d4db3fe304e9411a6651b128a25da260fce62)) + - Certain combinations of exclude specs could cause panics. ([`a2f1879`](https://github.com/Byron/gitoxide/commit/a2f1879f85df75146baf32336cf5a7b19dae032e)) + - Merge branch 'pathspec-matching' ([`9f4dfe0`](https://github.com/Byron/gitoxide/commit/9f4dfe0f0b948280692916b596923959ea2fd9da)) + - Match pathspecs just like `git` does. ([`49db3ac`](https://github.com/Byron/gitoxide/commit/49db3ac81db18fbc492d14161c09084112ff62f3)) + - `parse()` now supports the configuration of defaults. ([`f1421d3`](https://github.com/Byron/gitoxide/commit/f1421d31ab9e045f6b6599d21eb1a0723f90a242)) + - Add `Pattern::is_null()` to be able to check for a special kind of spec. ([`b3de72e`](https://github.com/Byron/gitoxide/commit/b3de72e014b33bdf1ebc39cd7e3e5db990e5b7f3)) + - Refactor ([`5f76ddf`](https://github.com/Byron/gitoxide/commit/5f76ddf0a5158ba5503a8a39b09336655eeb5156)) + - Release gix-glob v0.10.2, gix-date v0.7.2, gix-validate v0.8.0, gix-object v0.34.0, gix-ref v0.34.0, gix-config v0.27.0, gix-commitgraph v0.18.2, gix-revwalk v0.5.0, gix-revision v0.19.0, gix-refspec v0.15.0, gix-submodule v0.1.0, safety bump 18 crates ([`4604f83`](https://github.com/Byron/gitoxide/commit/4604f83ef238dc07c85aaeae097399b67f3cfd0c)) + - Merge branch 'dev-on-linux' ([`6b4a303`](https://github.com/Byron/gitoxide/commit/6b4a30330fe49fc97daa73f55bf56580cc0597aa)) + - Fix various tests to run properly on linux ([`ef8ccd9`](https://github.com/Byron/gitoxide/commit/ef8ccd9d16143d37155d063747c69cade80f162d)) + - Release gix-features v0.32.1, gix-actor v0.24.1, gix-validate v0.7.7, gix-object v0.33.1, gix-path v0.8.4, gix-glob v0.10.1, gix-quote v0.4.6, gix-attributes v0.16.0, gix-command v0.2.8, gix-packetline-blocking v0.16.4, gix-filter v0.2.0, gix-fs v0.4.1, gix-chunk v0.4.4, gix-commitgraph v0.18.1, gix-hashtable v0.2.4, gix-revwalk v0.4.1, gix-traverse v0.30.1, gix-worktree-stream v0.2.0, gix-archive v0.2.0, gix-config-value v0.12.5, gix-tempfile v7.0.1, gix-utils v0.1.5, gix-lock v7.0.2, gix-ref v0.33.1, gix-sec v0.8.4, gix-prompt v0.5.4, gix-url v0.21.1, gix-credentials v0.17.1, gix-diff v0.33.1, gix-discover v0.22.1, gix-ignore v0.5.1, gix-bitmap v0.2.6, gix-index v0.21.1, gix-mailmap v0.16.1, gix-negotiate v0.5.1, gix-pack v0.40.1, gix-odb v0.50.1, gix-packetline v0.16.4, gix-transport v0.34.1, gix-protocol v0.36.1, gix-revision v0.18.1, gix-refspec v0.14.1, gix-worktree v0.23.0, gix v0.50.0, safety bump 5 crates ([`16295b5`](https://github.com/Byron/gitoxide/commit/16295b58e2581d2e8b8b762816f52baabe871c75)) + - Update license field following SPDX 2.1 license expression standard ([`9064ea3`](https://github.com/Byron/gitoxide/commit/9064ea31fae4dc59a56bdd3a06c0ddc990ee689e)) + - Adjust package versions (by cargo-smart-release) ([`c70e54f`](https://github.com/Byron/gitoxide/commit/c70e54f163c312c87753a506eeaad462e8579bfb)) + - Release gix-glob v0.9.1, gix-attributes v0.14.1, gix-config-value v0.12.3, gix-ref v0.32.1, gix-sec v0.8.3, gix-config v0.25.1, gix-url v0.20.1, gix-credentials v0.16.1, gix-discover v0.21.1, gix-ignore v0.4.1, gix-pack v0.39.1, gix-odb v0.49.1, gix-worktree v0.21.1, gix v0.48.0 ([`69c6a36`](https://github.com/Byron/gitoxide/commit/69c6a36ba14cbef129deebda9fd8870005fefa17)) + - Release gix-date v0.6.0, gix-hash v0.11.3, gix-trace v0.1.1, gix-features v0.31.0, gix-actor v0.22.0, gix-path v0.8.2, gix-glob v0.9.0, gix-quote v0.4.5, gix-attributes v0.14.0, gix-chunk v0.4.3, gix-commitgraph v0.17.0, gix-config-value v0.12.2, gix-fs v0.3.0, gix-tempfile v7.0.0, gix-utils v0.1.3, gix-lock v7.0.0, gix-validate v0.7.6, gix-object v0.31.0, gix-ref v0.31.0, gix-sec v0.8.2, gix-config v0.24.0, gix-command v0.2.6, gix-prompt v0.5.2, gix-url v0.20.0, gix-credentials v0.16.0, gix-diff v0.31.0, gix-discover v0.20.0, gix-hashtable v0.2.2, gix-ignore v0.4.0, gix-bitmap v0.2.5, gix-revwalk v0.2.0, gix-traverse v0.28.0, gix-index v0.19.0, gix-mailmap v0.14.0, gix-negotiate v0.3.0, gix-pack v0.38.0, gix-odb v0.48.0, gix-packetline v0.16.3, gix-transport v0.33.0, gix-protocol v0.34.0, gix-revision v0.16.0, gix-refspec v0.12.0, gix-worktree v0.20.0, gix v0.47.0, gitoxide-core v0.29.0, gitoxide v0.27.0, safety bump 30 crates ([`ea9f942`](https://github.com/Byron/gitoxide/commit/ea9f9424e777f10da0e33bb9ffbbefd01c4c5a74)) + - Merge branch 'corpus' ([`aa16c8c`](https://github.com/Byron/gitoxide/commit/aa16c8ce91452a3e3063cf1cf0240b6014c4743f)) + - Change MSRV to 1.65 ([`4f635fc`](https://github.com/Byron/gitoxide/commit/4f635fc4429350bae2582d25de86429969d28f30)) + - Merge branch 'help-874-redundant-closures' ([`fe59956`](https://github.com/Byron/gitoxide/commit/fe59956ad667303a923d7cfd9ffd72283df41d78)) + - Add `clippy::redundant-closure-for-method-calls` lint ([`bcad5c2`](https://github.com/Byron/gitoxide/commit/bcad5c22049d56a25ef69d6c7a3344e78f9a1d4d)) + - Release gix-attributes v0.13.1, gix-diff v0.30.1, gix-revwalk v0.1.0, gix-traverse v0.27.0, gix-index v0.18.0, gix-revision v0.15.2, gix-negotiate v0.2.1, gix-pack v0.37.0, gix-odb v0.47.0, gix-protocol v0.33.2, gix-worktree v0.19.0, gix v0.46.0, safety bump 7 crates ([`2560a2c`](https://github.com/Byron/gitoxide/commit/2560a2cc3e1d8c60cd812e15696fa4761d036e19)) + - Release gix-date v0.5.1, gix-hash v0.11.2, gix-features v0.30.0, gix-actor v0.21.0, gix-path v0.8.1, gix-glob v0.8.0, gix-quote v0.4.4, gix-attributes v0.13.0, gix-chunk v0.4.2, gix-commitgraph v0.16.0, gix-config-value v0.12.1, gix-fs v0.2.0, gix-tempfile v6.0.0, gix-utils v0.1.2, gix-lock v6.0.0, gix-validate v0.7.5, gix-object v0.30.0, gix-ref v0.30.0, gix-sec v0.8.1, gix-config v0.23.0, gix-command v0.2.5, gix-prompt v0.5.1, gix-url v0.19.0, gix-credentials v0.15.0, gix-diff v0.30.0, gix-discover v0.19.0, gix-hashtable v0.2.1, gix-ignore v0.3.0, gix-bitmap v0.2.4, gix-traverse v0.26.0, gix-index v0.17.0, gix-mailmap v0.13.0, gix-revision v0.15.0, gix-negotiate v0.2.0, gix-pack v0.36.0, gix-odb v0.46.0, gix-packetline v0.16.2, gix-transport v0.32.0, gix-protocol v0.33.0, gix-refspec v0.11.0, gix-worktree v0.18.0, gix v0.45.0, safety bump 29 crates ([`9a9fa96`](https://github.com/Byron/gitoxide/commit/9a9fa96fa8a722bddc5c3b2270b0edf8f6615141)) + - Merge branch 'main' into auto-clippy ([`3ef5c90`](https://github.com/Byron/gitoxide/commit/3ef5c90aebce23385815f1df674c1d28d58b4b0d)) + - Merge branch 'blinxen/main' ([`9375cd7`](https://github.com/Byron/gitoxide/commit/9375cd75b01aa22a0e2eed6305fe45fabfd6c1ac)) + - Include license files in all crates ([`facaaf6`](https://github.com/Byron/gitoxide/commit/facaaf633f01c857dcf2572c6dbe0a92b7105c1c)) + - Bump gix-path v0.8.0, safety bump 20 crates (gix set to 0.44.1 manually) ([`43ebaf2`](https://github.com/Byron/gitoxide/commit/43ebaf267557218865862538ffc7bdf00558492f)) + - Merge branch 'fix-823' ([`6ebd61e`](https://github.com/Byron/gitoxide/commit/6ebd61e548a36a04e413ac725a03e607a3588334)) + - Thanks clippy ([`14e64e7`](https://github.com/Byron/gitoxide/commit/14e64e74649cfb1f2f99da87015939af98fae5c8)) + - Release gix-utils v0.1.0, gix-hash v0.11.0, gix-date v0.5.0, gix-features v0.29.0, gix-actor v0.20.0, gix-object v0.29.0, gix-archive v0.1.0, gix-fs v0.1.0, safety bump 25 crates ([`8dbd0a6`](https://github.com/Byron/gitoxide/commit/8dbd0a60557a85acfa231800a058cbac0271a8cf)) + - Merge branch 'patch-1' ([`d0052c1`](https://github.com/Byron/gitoxide/commit/d0052c13cabcde8058177d2439053b50ea5adbfc)) + - Update to latest `bitflags` version. ([`594cca5`](https://github.com/Byron/gitoxide/commit/594cca51840c00654af05acc7f7c7d01fe699067)) + - 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)) + - Adjust manifests prior to release ([`addd789`](https://github.com/Byron/gitoxide/commit/addd78958fdd1e54eb702854e96079539d01965a)) + - 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)) + - Release gix-glob v0.5.4 ([`c56d336`](https://github.com/Byron/gitoxide/commit/c56d3365fde21120cf6101cf34f8b5669804977c)) + - Merge branch 'rename-crates' into inform-about-gix-rename ([`c9275b9`](https://github.com/Byron/gitoxide/commit/c9275b99ea43949306d93775d9d78c98fb86cfb1)) + - Rename `git-testtools` to `gix-testtools` ([`b65c33d`](https://github.com/Byron/gitoxide/commit/b65c33d256cfed65d11adeff41132e3e58754089)) + - Adjust to renaming of `git-pathspec` to `gix-pathspec` ([`37f7c6b`](https://github.com/Byron/gitoxide/commit/37f7c6b9070e118604aa3fc0b38530699dcfec6e)) + - Renmae `git-pathspec` to `gix-pathspec` ([`a49ba33`](https://github.com/Byron/gitoxide/commit/a49ba33f7aa22e39b4177320224ee99353b7540b)) + - 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-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)) + - Adapt to renaming of `git-path` to `gix-path` ([`d3bbcfc`](https://github.com/Byron/gitoxide/commit/d3bbcfccad80fc44ea8e7bf819f23adaca06ba2d)) + - 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)) + - Thanks clippy ([`bac57dd`](https://github.com/Byron/gitoxide/commit/bac57dd05ea2d5a4ee45ef9350fa3f2e19474bc0)) + - 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)) + - 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)) + - 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)) + - Merge branch 'main' into read-split-index ([`c57bdde`](https://github.com/Byron/gitoxide/commit/c57bdde6de37eca9672ea715962bbd02aa3eb055)) + - Merge branch 'adjustments-for-cargo' ([`083909b`](https://github.com/Byron/gitoxide/commit/083909bc7eb902eeee2002034fdb6ed88280dc5c)) + - Adjust to changes in `git-testtools` ([`4eb842c`](https://github.com/Byron/gitoxide/commit/4eb842c7150b980e1c2637217e1f9657a671cea7)) + - Merge branch 'main' into http-config ([`bcd9654`](https://github.com/Byron/gitoxide/commit/bcd9654e56169799eb706646da6ee1f4ef2021a9)) + - Release git-hash v0.10.0, git-features v0.24.0, git-date v0.3.0, git-actor v0.14.0, git-glob v0.5.0, git-path v0.6.0, git-quote v0.4.0, git-attributes v0.6.0, git-config-value v0.9.0, git-tempfile v3.0.0, git-lock v3.0.0, git-validate v0.7.0, git-object v0.23.0, git-ref v0.20.0, git-sec v0.5.0, git-config v0.12.0, git-command v0.2.0, git-prompt v0.2.0, git-url v0.11.0, git-credentials v0.7.0, git-diff v0.23.0, git-discover v0.9.0, git-bitmap v0.2.0, git-traverse v0.19.0, git-index v0.9.0, git-mailmap v0.6.0, git-chunk v0.4.0, git-pack v0.27.0, git-odb v0.37.0, git-packetline v0.14.0, git-transport v0.23.0, git-protocol v0.24.0, git-revision v0.7.0, git-refspec v0.4.0, git-worktree v0.9.0, git-repository v0.29.0, git-commitgraph v0.11.0, gitoxide-core v0.21.0, gitoxide v0.19.0, safety bump 28 crates ([`b2c301e`](https://github.com/Byron/gitoxide/commit/b2c301ef131ffe1871314e19f387cf10a8d2ac16)) + - 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)) + - 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)) + - 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)) + - 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)) + - Merge branch 'diff' ([`25a7726`](https://github.com/Byron/gitoxide/commit/25a7726377fbe400ea3c4927d04e9dec99802b7b)) + - 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' ([`fd14489`](https://github.com/Byron/gitoxide/commit/fd14489f729172d615d0fa1e8dbd605e9eacf69d)) + - Merge branch 'index-from-tree' ([`172f73c`](https://github.com/Byron/gitoxide/commit/172f73cf26878d153d51790fa01853fa4ba6beb7)) + - Refactor ([`b2835cc`](https://github.com/Byron/gitoxide/commit/b2835cc28e10907eb375b2beb400cf408fa5a3e0)) + - 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-attributes v0.3.3, git-ref v0.15.3, git-index v0.4.3, git-worktree v0.4.3, git-testtools v0.8.0 ([`baad4ce`](https://github.com/Byron/gitoxide/commit/baad4ce51fe0e8c0c1de1b08148d8303878ca37b)) + - 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 '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)) + - Uniformize deny attributes ([`f7f136d`](https://github.com/Byron/gitoxide/commit/f7f136dbe4f86e7dee1d54835c420ec07c96cd78)) + - 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)) + - Merge branch 'rev-parse-delegate' ([`2f506c7`](https://github.com/Byron/gitoxide/commit/2f506c7c2988477b0f97d272a9ac9ed47b236457)) + - Merge pull request #2 from SidneyDouw/main ([`ce885ad`](https://github.com/Byron/gitoxide/commit/ce885ad4c3324c09c83751c32e014f246c748766)) + - Merge branch 'Byron:main' into main ([`9b9ea02`](https://github.com/Byron/gitoxide/commit/9b9ea0275f8ff5862f24cf5a4ca53bb1cd610709)) + - Merge branch 'main' into rev-parse-delegate ([`6da8250`](https://github.com/Byron/gitoxide/commit/6da82507588d3bc849217c11d9a1d398b67f2ed6)) + - Merge branch 'pathspec' ([`7db59a4`](https://github.com/Byron/gitoxide/commit/7db59a4074111086adfc2f79fd0d26bb30303ca9)) + - Avoid another vec allocation by inlining code via closure. ([`d88952a`](https://github.com/Byron/gitoxide/commit/d88952a533cf2fa2ebf0f015b10f5983a1c8f144)) + - Avoid temporary vec in favor of a `&'static [u8]`. ([`5a55dbf`](https://github.com/Byron/gitoxide/commit/5a55dbf5fd2ae8c28d95d72d55a30e4e7e2ef9cf)) + - Improve docs and use 'new-style' in error messages. ([`e36d83e`](https://github.com/Byron/gitoxide/commit/e36d83e62eb7969726e7c8b3d25dbb743a508f8a)) + - Assure all baseline samples are validated ([`4899722`](https://github.com/Byron/gitoxide/commit/489972209ee2ead2871ca2410bd10e51019dc9ad)) + - Add documentation; rename `SearchMode` to `MatchMode`; add test ([`4f6fa59`](https://github.com/Byron/gitoxide/commit/4f6fa59d20b4e03663f3d7c3819a7d02b79ab982)) + - Add basic docs for how to run the fuzzer ([`0c9bef4`](https://github.com/Byron/gitoxide/commit/0c9bef47a70a0787b63f9bf8e9b52f2ab9f72738)) + - Rename `parser` fuzz target to `parse` ([`ef03823`](https://github.com/Byron/gitoxide/commit/ef03823b407bcdbac16c42e941809dfe7cde850b)) + - Refactor ([`1cbc142`](https://github.com/Byron/gitoxide/commit/1cbc142d37599f4d7bfaf9cb07de41ee4b3f4c24)) + - Update crate status and READMe ([`07352ce`](https://github.com/Byron/gitoxide/commit/07352ceb942dac5041fbf8584128404880166593)) + - Refactor ([`63baa75`](https://github.com/Byron/gitoxide/commit/63baa752901388a46a4211c70f3b3a64aa36d4ec)) + - Add readme file ([`913d94c`](https://github.com/Byron/gitoxide/commit/913d94c3c226aac321de12eb7ae8bfa3ee3458e9)) + - Remove prefix stuff ([`b7baaa5`](https://github.com/Byron/gitoxide/commit/b7baaa5353aa52ca97488993b50cef72248387de)) + - Refactor of `Name` and `Assignment` ([`6449e77`](https://github.com/Byron/gitoxide/commit/6449e77e11ef0d25c2990f1c29e9fbea3c97fb0a)) + - Refactor ([`bffbcee`](https://github.com/Byron/gitoxide/commit/bffbcee68affce2a829c67bfe71e7df861c15ee5)) + - Fix unescaping logic - thanks fuzzer ([`9c6281f`](https://github.com/Byron/gitoxide/commit/9c6281f903451011407a352e1fca877d79c10466)) + - Add fuzzer ([`2a775c6`](https://github.com/Byron/gitoxide/commit/2a775c602da3edf20d84599839ca166bff2457a5)) + - Check attr values regarless of it being escaped ([`6e93144`](https://github.com/Byron/gitoxide/commit/6e93144545277cb5efe1dd3fba2ecaf90ea9c726)) + - Use "to_owned" instead of "into" ([`35c6d38`](https://github.com/Byron/gitoxide/commit/35c6d38088e09d88b30e12538e175a4a286980cd)) + - Help type inference ([`4d6befd`](https://github.com/Byron/gitoxide/commit/4d6befdbad72d1d143edd344cc7e9e0cba1d1e8a)) + - Fix build ([`1838f3d`](https://github.com/Byron/gitoxide/commit/1838f3db13eae6d278264dcdbc48d202de992349)) + - Refactor ([`1bdf2e1`](https://github.com/Byron/gitoxide/commit/1bdf2e1d1b8f58ed2d6fe21c62edb93fc5473c14)) + - Refactor ([`850bcc3`](https://github.com/Byron/gitoxide/commit/850bcc35f0e99ef1d65c6bd888638b9a67cab25b)) + - Refactor ([`65c8349`](https://github.com/Byron/gitoxide/commit/65c83491281184161ad6d9831adabb8475722e42)) + - Refactor attribute value unnescaping ([`24592f7`](https://github.com/Byron/gitoxide/commit/24592f7a4f6188d3bb0042f9e91dffc5fc01e382)) + - Implement name::error for git-attributes ([`0849ebf`](https://github.com/Byron/gitoxide/commit/0849ebf4bc2052d7886f9425800a547bf530e967)) + - Improved attribute value unescaping ([`1f89646`](https://github.com/Byron/gitoxide/commit/1f89646fc359f94ee001f5ed01623e5af7934a93)) + - Merge branch 'main' into pathspec ([`89ea12b`](https://github.com/Byron/gitoxide/commit/89ea12b558bcc056b892193ee8fb44b8664b5da4)) + - Refactor ([`9945ceb`](https://github.com/Byron/gitoxide/commit/9945ceb0a99c1343cb6e652e44900b36d3786e22)) + - Refactor ([`3b2bab8`](https://github.com/Byron/gitoxide/commit/3b2bab89172b86068bda9704bc9d69690bcfb2ba)) + - Refactor ([`852bcc3`](https://github.com/Byron/gitoxide/commit/852bcc316382fce5f5749942ecb43a73738ffe8f)) + - Protected attribute name via "AttributeName" type ([`7bb408e`](https://github.com/Byron/gitoxide/commit/7bb408e631138854a6dff85ce356da96f61367de)) + - Refactor ([`1ad98e8`](https://github.com/Byron/gitoxide/commit/1ad98e82f66c5f5eacb15f8ae38d8ccb1bc94e9e)) + - Escape attribute values in pathspec crate... ([`c22e57f`](https://github.com/Byron/gitoxide/commit/c22e57f8e4131655595da2b81662e23258bb85c8)) + - Refactor ([`7f00b50`](https://github.com/Byron/gitoxide/commit/7f00b50070c7b976a030ec836d2660ff5b7b5f72)) + - Refactor ([`2523f96`](https://github.com/Byron/gitoxide/commit/2523f9606f0adedb20ac93cf4853298bcd996118)) + - Refactor ([`699de03`](https://github.com/Byron/gitoxide/commit/699de03f0c981a9f8b5239c66dc425e504de1ec2)) + - Refactor ([`7f93231`](https://github.com/Byron/gitoxide/commit/7f93231b4983f9ce596cea84ad4525feb3778dd6)) + - Refactor ([`02fba2c`](https://github.com/Byron/gitoxide/commit/02fba2c124f3665112102469d41d476b6cf48dcd)) + - Improved testing against the baseline ([`44991d3`](https://github.com/Byron/gitoxide/commit/44991d373bd2e2f71ccf27eabe9f074cb5fe7c18)) + - Refactor ([`020bc24`](https://github.com/Byron/gitoxide/commit/020bc24973233edc261e05fd9935c5e598bf2922)) + - Refactor ([`b490b4a`](https://github.com/Byron/gitoxide/commit/b490b4a7be579941c2664fbefb25dd341ed7d1e7)) + - More testcases related to escape chars - still todo ([`f606515`](https://github.com/Byron/gitoxide/commit/f606515dfd89e28c78eaead3cf5023d5064618f5)) + - No splitting on escaped commas in attribute values ([`c0196fa`](https://github.com/Byron/gitoxide/commit/c0196fa363088426de031e55b982f70573ab738d)) + - Thanks clippy ([`f80eb85`](https://github.com/Byron/gitoxide/commit/f80eb851ab56b4580eb28935978487b9f37fe819)) + - Remove attr from signature bitflag ([`998415d`](https://github.com/Byron/gitoxide/commit/998415d9a3a234787c017bd448410a24ad4965f0)) + - Refactor ([`149d1b3`](https://github.com/Byron/gitoxide/commit/149d1b36f93d98002175cda362d50bac584c691a)) + - Whitespace test added ([`eecd388`](https://github.com/Byron/gitoxide/commit/eecd388708017414fee9077066f24124d83b70ba)) + - Refactor ([`476fa56`](https://github.com/Byron/gitoxide/commit/476fa56993391410fc0bafeccfcb8d4da8168bfc)) + - Add more test cases ([`9ceea27`](https://github.com/Byron/gitoxide/commit/9ceea2718a63bdd55ea8b99c2f1656cc09850145)) + - Thanks clippy ([`f7a3b69`](https://github.com/Byron/gitoxide/commit/f7a3b69e43e82471047091008355d180e646773d)) + - Added more tests ([`476f31c`](https://github.com/Byron/gitoxide/commit/476f31c0f0fc7b29d02110e3a9b9a542defce63e)) + - Refactor ([`d3ec61a`](https://github.com/Byron/gitoxide/commit/d3ec61a2fcc6d8269cb952def86a198a7ac9492e)) + - Refactor ([`5a3c0fe`](https://github.com/Byron/gitoxide/commit/5a3c0fe8d56e9bc28eda77d3d64ef5338365622c)) + - Refactor ([`162f9a0`](https://github.com/Byron/gitoxide/commit/162f9a06860fac69e6db6d76dc5051ec2e6ed2db)) + - Pattern now has searchmode... ([`0bed938`](https://github.com/Byron/gitoxide/commit/0bed9382930486af144876d97b97479e03e0f1c1)) + - Test refactor and bug fixes ([`57d8d90`](https://github.com/Byron/gitoxide/commit/57d8d90d246226fc9119612d10d31808f8fa3053)) + - Pattern uses MagigSignature without Option ([`f1f4ab3`](https://github.com/Byron/gitoxide/commit/f1f4ab3e3f50d00db4756ea724e5fd1b8ee75a04)) + - Error tests now use matches! ([`6a569a7`](https://github.com/Byron/gitoxide/commit/6a569a70d15c416f91ab20083747e25f867f7446)) + - Refactor ([`13b7db5`](https://github.com/Byron/gitoxide/commit/13b7db526c0a56360998a731ba10a7b4990d9529)) + - Refactor ([`2690b8a`](https://github.com/Byron/gitoxide/commit/2690b8a73c39175ccfddd098c1f72f2cdee048cf)) + - Merge branch 'main' into SidneyDouw-pathspec ([`a22b1d8`](https://github.com/Byron/gitoxide/commit/a22b1d88a21311d44509018729c3ef1936cf052a)) + - Hint for how to make a functional version bearable… ([`e8da186`](https://github.com/Byron/gitoxide/commit/e8da18663d5110c87200287a1bc0d1b6f86cf0f9)) + - Refactor ([`a0477e9`](https://github.com/Byron/gitoxide/commit/a0477e9b1fdf6ef289208a77f0539ea090c84e79)) + - Refactor ([`d109cfe`](https://github.com/Byron/gitoxide/commit/d109cfead637b0b2c2866fb411eeccbf6a5bff2c)) + - Refactor ([`fbed980`](https://github.com/Byron/gitoxide/commit/fbed980797057efb22140e8ff371989d49cc2a73)) + - Initial setup of pathspec module ([`fece972`](https://github.com/Byron/gitoxide/commit/fece9725d60201b16b67073c185195b88fa1ad20)) + - Release git-pathspec v0.0.0 ([`d6bee3f`](https://github.com/Byron/gitoxide/commit/d6bee3f931741906126a800aec9d43bc6bf8690f)) +</details> + +## 0.0.0 (2022-03-17) + +An empty crate without any content to reserve the name for the gitoxide project. + diff --git a/vendor/gix-pathspec/Cargo.toml b/vendor/gix-pathspec/Cargo.toml new file mode 100644 index 000000000..57527a4f4 --- /dev/null +++ b/vendor/gix-pathspec/Cargo.toml @@ -0,0 +1,53 @@ +# 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.65" +name = "gix-pathspec" +version = "0.3.0" +authors = ["Sebastian Thiel <sebastian.thiel@icloud.com>"] +description = "A crate of the gitoxide project dealing magical pathspecs" +readme = "README.md" +license = "MIT OR Apache-2.0" +repository = "https://github.com/Byron/gitoxide" + +[lib] +doctest = false + +[dependencies.bitflags] +version = "2" + +[dependencies.bstr] +version = "1.3.0" +features = ["std"] +default-features = false + +[dependencies.gix-attributes] +version = "^0.19.0" + +[dependencies.gix-config-value] +version = "^0.14.0" + +[dependencies.gix-glob] +version = "^0.13.0" + +[dependencies.gix-path] +version = "^0.10.0" + +[dependencies.thiserror] +version = "1.0.26" + +[dev-dependencies.once_cell] +version = "1.12.0" + +[dev-dependencies.serial_test] +version = "2.0.0" diff --git a/vendor/gix-pathspec/LICENSE-APACHE b/vendor/gix-pathspec/LICENSE-APACHE new file mode 100644 index 000000000..a51f59a06 --- /dev/null +++ b/vendor/gix-pathspec/LICENSE-APACHE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2018-2021 Sebastian Thiel, and [contributors](https://github.com/byron/gitoxide/contributors) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/gix-pathspec/LICENSE-MIT b/vendor/gix-pathspec/LICENSE-MIT new file mode 100644 index 000000000..b58e818f1 --- /dev/null +++ b/vendor/gix-pathspec/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018-2021 Sebastian Thiel, and [contributors](https://github.com/byron/gitoxide/contributors). + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/gix-pathspec/README.md b/vendor/gix-pathspec/README.md new file mode 100644 index 000000000..ecaa8f56d --- /dev/null +++ b/vendor/gix-pathspec/README.md @@ -0,0 +1,16 @@ +# `gix-pathspec` + +### Testing + +#### Fuzzing + +`cargo fuzz` is used for fuzzing, installable with `cargo install cargo-fuzz`. + +Targets can be listed with `cargo fuzz list` and executed via `cargo +nightly fuzz run <target>`, +where `<target>` can be `parse` for example. + +### Notes + +- There is one additional keyword that `git` can parse, but that this crate doesn't support yet: the `prefix` keyword + + [Here is a commit](https://github.com/git/git/commit/5be4efbefafcd5b81fe3d97e8395da1887b4902a) in which `prefix` is somewhat explained. diff --git a/vendor/gix-pathspec/src/defaults.rs b/vendor/gix-pathspec/src/defaults.rs new file mode 100644 index 000000000..81be53b38 --- /dev/null +++ b/vendor/gix-pathspec/src/defaults.rs @@ -0,0 +1,70 @@ +use std::ffi::OsString; + +use crate::{Defaults, MagicSignature, SearchMode}; + +/// +pub mod from_environment { + /// The error returned by [Defaults::from_environment()](super::Defaults::from_environment()). + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error(transparent)] + ParseValue(#[from] gix_config_value::Error), + #[error("Glob and no-glob settings are mutually exclusive")] + MixedGlobAndNoGlob, + } +} + +impl Defaults { + /// Initialize this instance using information from the environment as + /// [per the official documentation](https://git-scm.com/book/en/v2/Git-Internals-Environment-Variables) *(look for `PATHSPECS`)*, + /// calling `var(variable_name)` for each variable that should be obtained. + /// + /// Used environment variables are `GIT_GLOB_PATHSPECS`, `GIT_NOGLOB_PATHSPECS`, `GIT_LITERAL_PATHSPECS` and `GIT_ICASE_PATHSPECS`. + /// Note that there are lot of failure modes, and instead of offering lenient parsing, the caller may ignore errors and + /// use other defaults instead. + /// + /// ### Deviation + /// + /// Instead of failing if `GIT_LITERAL_PATHSPECS` is used with glob globals, we ignore these. Also our implementation allows global + /// `icase` settings in combination with this setting. + pub fn from_environment(var: &mut dyn FnMut(&str) -> Option<OsString>) -> Result<Self, from_environment::Error> { + let mut env_bool = |name: &str| -> Result<Option<bool>, gix_config_value::Error> { + var(name) + .map(|val| gix_config_value::Boolean::try_from(val).map(|b| b.0)) + .transpose() + }; + + let literal = env_bool("GIT_LITERAL_PATHSPECS")?.unwrap_or_default(); + let signature = env_bool("GIT_ICASE_PATHSPECS")? + .and_then(|val| val.then_some(MagicSignature::ICASE)) + .unwrap_or_default(); + if literal { + return Ok(Defaults { + signature, + search_mode: SearchMode::Literal, + literal, + }); + } + let glob = env_bool("GIT_GLOB_PATHSPECS")?; + let mut search_mode = glob + .and_then(|glob| glob.then_some(SearchMode::PathAwareGlob)) + .unwrap_or_default(); + search_mode = env_bool("GIT_NOGLOB_PATHSPECS")? + .map(|no_glob| { + if glob.unwrap_or_default() && no_glob { + Err(from_environment::Error::MixedGlobAndNoGlob) + } else { + Ok(SearchMode::Literal) + } + }) + .transpose()? + .unwrap_or(search_mode); + + Ok(Defaults { + signature, + search_mode, + literal, + }) + } +} diff --git a/vendor/gix-pathspec/src/lib.rs b/vendor/gix-pathspec/src/lib.rs new file mode 100644 index 000000000..9f91567da --- /dev/null +++ b/vendor/gix-pathspec/src/lib.rs @@ -0,0 +1,143 @@ +//! Parse [path specifications](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefpathspecapathspec) and +//! see if a path matches. +#![deny(missing_docs, rust_2018_idioms)] +#![forbid(unsafe_code)] + +use std::path::PathBuf; + +use bitflags::bitflags; +use bstr::BString; +/// `gix-glob` types are available through [`attributes::glob`]. +pub use gix_attributes as attributes; + +/// +pub mod normalize { + use std::path::PathBuf; + + /// The error returned by [Pattern::normalize()](super::Pattern::normalize()). + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error("The path '{}' is not inside of the worktree '{}'", path.display(), worktree_path.display())] + AbsolutePathOutsideOfWorktree { path: PathBuf, worktree_path: PathBuf }, + #[error("The path '{}' leaves the repository", path.display())] + OutsideOfWorktree { path: PathBuf }, + } +} + +mod pattern; + +/// +pub mod search; + +/// +pub mod parse; + +/// Default settings for some fields of a [`Pattern`]. +/// +/// These can be used to represent `GIT_*_PATHSPECS` environment variables, for example. +#[derive(Debug, Default, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +pub struct Defaults { + /// The default signature. + pub signature: MagicSignature, + /// The default search-mode. + /// + /// Note that even if it's [`SearchMode::Literal`], the pathspecs will be parsed as usual, but matched verbatim afterwards. + /// + /// Note that pathspecs can override this the [`SearchMode::Literal`] variant with an explicit `:(glob)` prefix. + pub search_mode: SearchMode, + /// If set, the pathspec will not be parsed but used verbatim. Implies [`SearchMode::Literal`] for `search_mode`. + pub literal: bool, +} + +/// +pub mod defaults; + +/// A lists of pathspec patterns, possibly from a file. +/// +/// Pathspecs are generally relative to the root of the repository. +#[derive(Debug, Clone)] +pub struct Search { + /// Patterns and their associated data in the order they were loaded in or specified, + /// the line number in its source file or its sequence number (_`(pattern, value, line_number)`_). + /// + /// During matching, this order is reversed. + patterns: Vec<gix_glob::search::pattern::Mapping<search::Spec>>, + + /// The path from which the patterns were read, or `None` if the patterns + /// don't originate in a file on disk. + pub source: Option<PathBuf>, + + /// If `true`, this means all `patterns` are exclude patterns. This means that if there is no match + /// (which would exclude an item), we would actually match it for lack of exclusion. + all_patterns_are_excluded: bool, + /// The amount of bytes that are in common among all `patterns` and that aren't matched case-insensitively + common_prefix_len: usize, +} + +/// The output of a pathspec [parsing][parse()] operation. It can be used to match against a one or more paths. +#[derive(Default, PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)] +pub struct Pattern { + /// The path part of a pathspec, which is typically a path possibly mixed with glob patterns. + /// Note that it might be an empty string as well. + /// + /// For example, `:(top,literal,icase,attr,exclude)some/path` would yield `some/path`. + path: BString, + /// All magic signatures that were included in the pathspec. + pub signature: MagicSignature, + /// The search mode of the pathspec. + pub search_mode: SearchMode, + /// All attributes that were included in the `ATTR` part of the pathspec, if present. + /// + /// `:(attr:a=one b=):path` would yield attribute `a` and `b`. + pub attributes: Vec<gix_attributes::Assignment>, + /// If `true`, we are a special Nil pattern and always match. + nil: bool, + /// The length of bytes in `path` that belong to the prefix, which will always be matched case-sensitively + /// on case-sensitive filesystems. + /// + /// That way, even though pathspecs are applied from the top, we can emulate having changed directory into + /// a specific sub-directory in a case-sensitive file-system, even if the rest of the pathspec can be set to + /// match case-insensitively. + /// Is set by [Pattern::normalize()]. + prefix_len: usize, +} + +bitflags! { + /// Flags to represent 'magic signatures' which are parsed behind colons, like `:top:`. + #[derive(Default, PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] + pub struct MagicSignature: u32 { + /// Matches patterns from the root of the repository + const TOP = 1 << 0; + /// Matches patterns in case insensitive mode + const ICASE = 1 << 1; + /// Excludes the matching patterns from the previous results + const EXCLUDE = 1 << 2; + /// The pattern must match a directory, and not a file. + /// This is equivalent to how it's handled in `gix-glob` + const MUST_BE_DIR = 1 << 3; + } +} + +/// Parts of [magic signatures][MagicSignature] which don't stack as they all configure +/// the way path specs are matched. +#[derive(Default, PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] +pub enum SearchMode { + /// Expand special characters like `*` similar to how the shell would do it. + /// + /// See [`PathAwareGlob`](SearchMode::PathAwareGlob) for the alternative. + #[default] + ShellGlob, + /// Special characters in the pattern, like `*` or `?`, are treated literally, effectively turning off globbing. + Literal, + /// A single `*` will not match a `/` in the pattern, but a `**` will + PathAwareGlob, +} + +/// Parse a git-style pathspec into a [`Pattern`][Pattern], +/// setting the given `default` values in case these aren't specified in `input`. +/// +/// Note that empty [paths](Pattern::path) are allowed here, and generally some processing has to be performed. +pub fn parse(input: &[u8], default: Defaults) -> Result<Pattern, parse::Error> { + Pattern::from_bytes(input, default) +} diff --git a/vendor/gix-pathspec/src/parse.rs b/vendor/gix-pathspec/src/parse.rs new file mode 100644 index 000000000..9a21511ca --- /dev/null +++ b/vendor/gix-pathspec/src/parse.rs @@ -0,0 +1,263 @@ +use std::borrow::Cow; + +use bstr::{BStr, BString, ByteSlice, ByteVec}; + +use crate::{Defaults, MagicSignature, Pattern, SearchMode}; + +/// The error returned by [parse()][crate::parse()]. +#[derive(thiserror::Error, Debug)] +#[allow(missing_docs)] +pub enum Error { + #[error("An empty string is not a valid pathspec")] + EmptyString, + #[error("Found {keyword:?} in signature, which is not a valid keyword")] + InvalidKeyword { keyword: BString }, + #[error("Unimplemented short keyword: {short_keyword:?}")] + Unimplemented { short_keyword: char }, + #[error("Missing ')' at the end of pathspec signature")] + MissingClosingParenthesis, + #[error("Attribute has non-ascii characters or starts with '-': {attribute:?}")] + InvalidAttribute { attribute: BString }, + #[error("Invalid character in attribute value: {character:?}")] + InvalidAttributeValue { character: char }, + #[error("Escape character '\\' is not allowed as the last character in an attribute value")] + TrailingEscapeCharacter, + #[error("Attribute specification cannot be empty")] + EmptyAttribute, + #[error("Only one attribute specification is allowed in the same pathspec")] + MultipleAttributeSpecifications, + #[error("'literal' and 'glob' keywords cannot be used together in the same pathspec")] + IncompatibleSearchModes, +} + +impl Pattern { + /// Try to parse a path-spec pattern from the given `input` bytes. + pub fn from_bytes( + input: &[u8], + Defaults { + signature, + search_mode, + literal, + }: Defaults, + ) -> Result<Self, Error> { + if input.is_empty() { + return Err(Error::EmptyString); + } + if literal { + return Ok(Self::from_literal(input, signature)); + } + if input.as_bstr() == ":" { + return Ok(Pattern { + nil: true, + ..Default::default() + }); + } + + let mut p = Pattern { + signature, + search_mode: SearchMode::default(), + ..Default::default() + }; + + let mut cursor = 0; + if input.first() == Some(&b':') { + cursor += 1; + p.signature |= parse_short_keywords(input, &mut cursor)?; + if let Some(b'(') = input.get(cursor) { + cursor += 1; + parse_long_keywords(input, &mut p, &mut cursor)?; + } + } + + if search_mode != Default::default() && p.search_mode == Default::default() { + p.search_mode = search_mode; + } + let mut path = &input[cursor..]; + if path.last() == Some(&b'/') { + p.signature |= MagicSignature::MUST_BE_DIR; + path = &path[..path.len() - 1]; + } + p.path = path.into(); + Ok(p) + } + + /// Take `input` literally without parsing anything. This will also set our mode to `literal` to allow this pathspec to match `input` verbatim, and + /// use `default_signature` as magic signature. + pub fn from_literal(input: &[u8], default_signature: MagicSignature) -> Self { + Pattern { + path: input.into(), + signature: default_signature, + search_mode: SearchMode::Literal, + ..Default::default() + } + } +} + +fn parse_short_keywords(input: &[u8], cursor: &mut usize) -> Result<MagicSignature, Error> { + let unimplemented_chars = b"\"#%&'-',;<=>@_`~"; + + let mut signature = MagicSignature::empty(); + while let Some(&b) = input.get(*cursor) { + *cursor += 1; + signature |= match b { + b'/' => MagicSignature::TOP, + b'^' | b'!' => MagicSignature::EXCLUDE, + b':' => break, + _ if unimplemented_chars.contains(&b) => { + return Err(Error::Unimplemented { + short_keyword: b.into(), + }); + } + _ => { + *cursor -= 1; + break; + } + } + } + + Ok(signature) +} + +fn parse_long_keywords(input: &[u8], p: &mut Pattern, cursor: &mut usize) -> Result<(), Error> { + let end = input.find(")").ok_or(Error::MissingClosingParenthesis)?; + + let input = &input[*cursor..end]; + *cursor = end + 1; + + if input.is_empty() { + return Ok(()); + } + + split_on_non_escaped_char(input, b',', |keyword| { + let attr_prefix = b"attr:"; + match keyword { + b"attr" => {} + b"top" => p.signature |= MagicSignature::TOP, + b"icase" => p.signature |= MagicSignature::ICASE, + b"exclude" => p.signature |= MagicSignature::EXCLUDE, + b"literal" => match p.search_mode { + SearchMode::PathAwareGlob => return Err(Error::IncompatibleSearchModes), + _ => p.search_mode = SearchMode::Literal, + }, + b"glob" => match p.search_mode { + SearchMode::Literal => return Err(Error::IncompatibleSearchModes), + _ => p.search_mode = SearchMode::PathAwareGlob, + }, + _ if keyword.starts_with(attr_prefix) => { + if p.attributes.is_empty() { + p.attributes = parse_attributes(&keyword[attr_prefix.len()..])?; + } else { + return Err(Error::MultipleAttributeSpecifications); + } + } + _ => { + return Err(Error::InvalidKeyword { + keyword: BString::from(keyword), + }); + } + }; + Ok(()) + }) +} + +fn split_on_non_escaped_char( + input: &[u8], + split_char: u8, + mut f: impl FnMut(&[u8]) -> Result<(), Error>, +) -> Result<(), Error> { + let mut i = 0; + let mut last = 0; + for window in input.windows(2) { + i += 1; + if window[0] != b'\\' && window[1] == split_char { + let keyword = &input[last..i]; + f(keyword)?; + last = i + 1; + } + } + let last_keyword = &input[last..]; + f(last_keyword) +} + +fn parse_attributes(input: &[u8]) -> Result<Vec<gix_attributes::Assignment>, Error> { + if input.is_empty() { + return Err(Error::EmptyAttribute); + } + + let unescaped = unescape_attribute_values(input.into())?; + + gix_attributes::parse::Iter::new(unescaped.as_bstr()) + .map(|res| res.map(gix_attributes::AssignmentRef::to_owned)) + .collect::<Result<Vec<_>, _>>() + .map_err(|e| Error::InvalidAttribute { attribute: e.attribute }) +} + +fn unescape_attribute_values(input: &BStr) -> Result<Cow<'_, BStr>, Error> { + if !input.contains(&b'=') { + return Ok(Cow::Borrowed(input)); + } + + let mut out: Cow<'_, BStr> = Cow::Borrowed("".into()); + + for attr in input.split(|&c| c == b' ') { + let split_point = attr.find_byte(b'=').map_or_else(|| attr.len(), |i| i + 1); + let (name, value) = attr.split_at(split_point); + + if value.contains(&b'\\') { + let out = out.to_mut(); + out.push_str(name); + out.push_str(unescape_and_check_attr_value(value.into())?); + out.push(b' '); + } else { + check_attribute_value(value.as_bstr())?; + match out { + Cow::Borrowed(_) => { + let end = out.len() + attr.len() + 1; + out = Cow::Borrowed(&input[0..end.min(input.len())]); + } + Cow::Owned(_) => { + let out = out.to_mut(); + out.push_str(name); + out.push_str(value); + out.push(b' '); + } + } + } + } + + Ok(out) +} + +fn unescape_and_check_attr_value(value: &BStr) -> Result<BString, Error> { + let mut out = BString::from(Vec::with_capacity(value.len())); + let mut bytes = value.iter(); + while let Some(mut b) = bytes.next().copied() { + if b == b'\\' { + b = *bytes.next().ok_or(Error::TrailingEscapeCharacter)?; + } + + out.push(validated_attr_value_byte(b)?); + } + Ok(out) +} + +fn check_attribute_value(input: &BStr) -> Result<(), Error> { + match input.iter().copied().find(|b| !is_valid_attr_value(*b)) { + Some(b) => Err(Error::InvalidAttributeValue { character: b as char }), + None => Ok(()), + } +} + +fn is_valid_attr_value(byte: u8) -> bool { + byte.is_ascii_alphanumeric() || b",-_".contains(&byte) +} + +fn validated_attr_value_byte(byte: u8) -> Result<u8, Error> { + if is_valid_attr_value(byte) { + Ok(byte) + } else { + Err(Error::InvalidAttributeValue { + character: byte as char, + }) + } +} diff --git a/vendor/gix-pathspec/src/pattern.rs b/vendor/gix-pathspec/src/pattern.rs new file mode 100644 index 000000000..09fd058f1 --- /dev/null +++ b/vendor/gix-pathspec/src/pattern.rs @@ -0,0 +1,194 @@ +use std::path::{Component, Path, PathBuf}; + +use bstr::{BStr, BString, ByteSlice, ByteVec}; + +use crate::{normalize, MagicSignature, Pattern, SearchMode}; + +/// Access +impl Pattern { + /// Returns `true` if this seems to be a pathspec that indicates that 'there is no pathspec'. + /// + /// Note that such a spec is `:`. + pub fn is_nil(&self) -> bool { + self.nil + } + + /// Return the prefix-portion of the `path` of this spec, which is a *directory*. + /// It can be empty if there is no prefix. + /// + /// A prefix is effectively the CWD seen as relative to the working tree, and it's assumed to + /// match case-sensitively. This makes it useful for skipping over large portions of input by + /// directly comparing them. + pub fn prefix_directory(&self) -> &BStr { + self.path[..self.prefix_len].as_bstr() + } + + /// Return the path of this spec, typically used for matching. + pub fn path(&self) -> &BStr { + self.path.as_ref() + } +} + +/// Mutation +impl Pattern { + /// Normalize the pattern's path by assuring it's relative to the root of the working tree, and contains + /// no relative path components. Further, it assures that `/` are used as path separator. + /// + /// If `self.path` is a relative path, it will be put in front of the pattern path if `self.signature` isn't indicating `TOP` already. + /// If `self.path` is an absolute path, we will use `root` to make it worktree relative if possible. + /// + /// `prefix` can be empty, we will still normalize this pathspec to resolve relative path components, and + /// it is assumed not to contain any relative path components, e.g. '', 'a', 'a/b' are valid. + /// `root` is the absolute path to the root of either the worktree or the repository's `git_dir`. + pub fn normalize(&mut self, prefix: &Path, root: &Path) -> Result<&mut Self, normalize::Error> { + fn prefix_components_to_subtract(path: &Path) -> usize { + let parent_component_end_bound = path.components().enumerate().fold(None::<usize>, |acc, (idx, c)| { + matches!(c, Component::ParentDir).then_some(idx + 1).or(acc) + }); + let count = path + .components() + .take(parent_component_end_bound.unwrap_or(0)) + .map(|c| match c { + Component::ParentDir => 1_isize, + Component::Normal(_) => -1, + _ => 0, + }) + .sum::<isize>(); + (count > 0).then_some(count as usize).unwrap_or_default() + } + + let mut path = gix_path::from_bstr(self.path.as_bstr()); + let mut num_prefix_components = 0; + let mut was_absolute = false; + if gix_path::is_absolute(path.as_ref()) { + was_absolute = true; + let rela_path = match path.strip_prefix(root) { + Ok(path) => path, + Err(_) => { + return Err(normalize::Error::AbsolutePathOutsideOfWorktree { + path: path.into_owned(), + worktree_path: root.into(), + }) + } + }; + path = rela_path.to_owned().into(); + } else if !prefix.as_os_str().is_empty() && !self.signature.contains(MagicSignature::TOP) { + debug_assert_eq!( + prefix + .components() + .filter(|c| matches!(c, Component::Normal(_))) + .count(), + prefix.components().count(), + "BUG: prefixes must not have relative path components, or calculations here will be wrong so pattern won't match" + ); + num_prefix_components = prefix + .components() + .count() + .saturating_sub(prefix_components_to_subtract(path.as_ref())); + path = prefix.join(path).into(); + } + + let assure_path_cannot_break_out_upwards = Path::new(""); + let path = match gix_path::normalize(path.as_ref().into(), assure_path_cannot_break_out_upwards) { + Some(path) => { + if was_absolute { + num_prefix_components = path.components().count().saturating_sub( + if self.signature.contains(MagicSignature::MUST_BE_DIR) { + 0 + } else { + 1 + }, + ); + } + path + } + None => { + return Err(normalize::Error::OutsideOfWorktree { + path: path.into_owned(), + }) + } + }; + + self.path = if path == Path::new(".") { + BString::from(".") + } else { + let cleaned = PathBuf::from_iter(path.components().filter(|c| !matches!(c, Component::CurDir))); + let mut out = gix_path::to_unix_separators_on_windows(gix_path::into_bstr(cleaned)).into_owned(); + self.prefix_len = { + if self.signature.contains(MagicSignature::MUST_BE_DIR) { + out.push(b'/'); + } + let len = out + .find_iter(b"/") + .take(num_prefix_components) + .last() + .unwrap_or_default(); + if self.signature.contains(MagicSignature::MUST_BE_DIR) { + out.pop(); + } + len + }; + out + }; + + Ok(self) + } +} + +/// Access +impl Pattern { + /// Return `true` if this pathspec is negated, which means it will exclude an item from the result set instead of including it. + pub fn is_excluded(&self) -> bool { + self.signature.contains(MagicSignature::EXCLUDE) + } + + /// Translate ourselves to a long display format, that when parsed back will yield the same pattern. + /// + /// Note that the + pub fn to_bstring(&self) -> BString { + if self.is_nil() { + ":".into() + } else { + let mut buf: BString = ":(".into(); + if self.signature.contains(MagicSignature::TOP) { + buf.push_str("top,"); + } + if self.signature.contains(MagicSignature::EXCLUDE) { + buf.push_str("exclude,"); + } + if self.signature.contains(MagicSignature::ICASE) { + buf.push_str("icase,"); + } + match self.search_mode { + SearchMode::ShellGlob => {} + SearchMode::Literal => buf.push_str("literal,"), + SearchMode::PathAwareGlob => buf.push_str("glob,"), + } + if self.attributes.is_empty() { + if buf.last() == Some(&b',') { + buf.pop(); + } + } else { + buf.push_str("attr:"); + for attr in &self.attributes { + let attr = attr.as_ref().to_string().replace(',', "\\,"); + buf.push_str(&attr); + buf.push(b' '); + } + buf.pop(); // trailing ' ' + } + buf.push(b')'); + buf.extend_from_slice(&self.path); + if self.signature.contains(MagicSignature::MUST_BE_DIR) { + buf.push(b'/'); + } + buf + } + } +} + +impl std::fmt::Display for Pattern { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.to_bstring().fmt(f) + } +} diff --git a/vendor/gix-pathspec/src/search/init.rs b/vendor/gix-pathspec/src/search/init.rs new file mode 100644 index 000000000..bc252f230 --- /dev/null +++ b/vendor/gix-pathspec/src/search/init.rs @@ -0,0 +1,146 @@ +use std::path::Path; + +use crate::{search::Spec, MagicSignature, Pattern, Search}; + +/// Create a new specification to support matches from `pathspec`, [normalizing](Pattern::normalize()) it with `prefix` and `root`. +fn mapping_from_pattern( + mut pathspec: Pattern, + prefix: &Path, + root: &Path, + sequence_number: usize, +) -> Result<gix_glob::search::pattern::Mapping<Spec>, crate::normalize::Error> { + pathspec.normalize(prefix, root)?; + let mut match_all = pathspec.is_nil(); + let glob = { + let mut g = gix_glob::Pattern::from_bytes_without_negation(&pathspec.path).unwrap_or_else(|| { + match_all = true; + // This pattern is setup to match literally as all-whitespace. + gix_glob::Pattern { + text: pathspec.path.clone(), + mode: gix_glob::pattern::Mode::empty(), + first_wildcard_pos: None, + } + }); + g.mode |= gix_glob::pattern::Mode::ABSOLUTE; + if pathspec.signature.contains(MagicSignature::MUST_BE_DIR) { + g.mode |= gix_glob::pattern::Mode::MUST_BE_DIR; + } + g + }; + + Ok(gix_glob::search::pattern::Mapping { + pattern: glob, + value: Spec { + attrs_match: { + (!pathspec.attributes.is_empty()).then(|| { + let mut out = gix_attributes::search::Outcome::default(); + out.initialize_with_selection( + &Default::default(), + pathspec.attributes.iter().map(|a| a.name.as_str()), + ); + out + }) + }, + pattern: pathspec, + }, + sequence_number, + }) +} + +fn common_prefix_len(patterns: &[gix_glob::search::pattern::Mapping<Spec>]) -> usize { + let mut count = 0; + let len = patterns + .iter() + .filter(|p| !p.value.pattern.is_excluded()) + .map(|p| { + count += 1; + if p.value.pattern.signature.contains(MagicSignature::ICASE) { + p.value.pattern.prefix_len + } else { + p.pattern.first_wildcard_pos.unwrap_or(p.pattern.text.len()) + } + }) + .min() + .unwrap_or_default(); + + if len == 0 { + return 0; + } + + let mut max_len = len; + if count < 2 { + return max_len; + } + + let mut patterns = patterns + .iter() + .filter(|p| !p.value.pattern.is_excluded()) + .map(|p| &p.value.pattern.path); + let base = &patterns.next().expect("at least two patterns"); + for path in patterns { + for (idx, (a, b)) in base[..max_len].iter().zip(path[..max_len].iter()).enumerate() { + if *a != *b { + max_len = idx; + break; + } + } + } + max_len +} + +/// Lifecycle +impl Search { + /// Create a search from ready-made `pathspecs`, and [normalize](Pattern::normalize()) them with `prefix` and `root`. + /// `root` is the absolute path to the worktree root, if available, or the `git_dir` in case of bare repositories. + /// If `pathspecs` doesn't yield any pattern, we will match everything automatically. If `prefix` is also provided and not empty, + /// an artificial pattern will be added to yield all. + pub fn from_specs( + pathspecs: impl IntoIterator<Item = Pattern>, + prefix: Option<&std::path::Path>, + root: &std::path::Path, + ) -> Result<Self, crate::normalize::Error> { + fn inner( + pathspecs: &mut dyn Iterator<Item = Pattern>, + prefix: Option<&std::path::Path>, + root: &std::path::Path, + ) -> Result<Search, crate::normalize::Error> { + let prefix = prefix.unwrap_or(std::path::Path::new("")); + let mut patterns = pathspecs + .enumerate() + .map(|(idx, pattern)| mapping_from_pattern(pattern, prefix, root, idx)) + .collect::<Result<Vec<_>, _>>()?; + + if patterns.is_empty() && !prefix.as_os_str().is_empty() { + patterns.push(mapping_from_pattern( + Pattern::from_literal(&[], MagicSignature::MUST_BE_DIR), + prefix, + root, + 0, + )?); + } + + // Excludes should always happen first so we know a match is authoritative (otherwise we could find a non-excluding match first). + patterns.sort_by(|a, b| { + a.value + .pattern + .is_excluded() + .cmp(&b.value.pattern.is_excluded()) + .reverse() + }); + + let common_prefix_len = common_prefix_len(&patterns); + Ok(Search { + all_patterns_are_excluded: patterns.iter().all(|s| s.value.pattern.is_excluded()), + patterns, + source: None, + common_prefix_len, + }) + } + inner(&mut pathspecs.into_iter(), prefix, root) + } + + /// Obtain ownership of the normalized pathspec patterns that were used for the search. + pub fn into_patterns(self) -> impl Iterator<Item = Pattern> { + self.patterns.into_iter().map(|p| p.value.pattern) + } +} diff --git a/vendor/gix-pathspec/src/search/matching.rs b/vendor/gix-pathspec/src/search/matching.rs new file mode 100644 index 000000000..c7c8f2cbb --- /dev/null +++ b/vendor/gix-pathspec/src/search/matching.rs @@ -0,0 +1,149 @@ +use bstr::{BStr, BString, ByteSlice}; +use gix_glob::pattern::Case; + +use crate::{ + search::{Match, Spec}, + MagicSignature, Pattern, Search, SearchMode, +}; + +impl Search { + /// Return the first [`Match`] of `relative_path`, or `None`. + /// `is_dir` is `true` if `relative_path` is a directory. + /// `attributes` is called as `attributes(relative_path, case, is_dir, outcome) -> has_match` to obtain for attributes for `relative_path`, if + /// the underlying pathspec defined an attribute filter, to be stored in `outcome`, returning true if there was a match. + /// All attributes of the pathspec have to be present in the defined value for the pathspec to match. + /// + /// Note that `relative_path` is expected to be starting at the same root as is assumed for this pattern, see [`Pattern::normalize()`]. + /// Further, empty searches match everything, as if `:` was provided. + /// + /// ### Deviation + /// + /// The case-sensivity of the attribute match is controlled by the sensitivity of the pathspec, instead of being based on the + /// case folding settings of the repository. That way we assure that the matching is consistent. + /// Higher-level crates should control this default case folding of pathspecs when instantiating them, which is when they can + /// set it to match the repository setting for more natural behaviour when, for instance, adding files to a repository: + /// as it stands, on a case-insensitive file system, `touch File && git add file` will not add the file, but also not error. + pub fn pattern_matching_relative_path( + &mut self, + relative_path: &BStr, + is_dir: Option<bool>, + attributes: &mut dyn FnMut(&BStr, Case, bool, &mut gix_attributes::search::Outcome) -> bool, + ) -> Option<Match<'_>> { + let basename_not_important = None; + if relative_path + .get(..self.common_prefix_len) + .map_or(true, |rela_path_prefix| rela_path_prefix != self.common_prefix()) + { + return None; + } + + let is_dir = is_dir.unwrap_or(false); + let patterns_len = self.patterns.len(); + let res = self.patterns.iter_mut().find_map(|mapping| { + let ignore_case = mapping.value.pattern.signature.contains(MagicSignature::ICASE); + let prefix = mapping.value.pattern.prefix_directory(); + if ignore_case && !prefix.is_empty() { + let pattern_requirement_is_met = relative_path.get(prefix.len()).map_or_else(|| is_dir, |b| *b == b'/'); + if !pattern_requirement_is_met + || relative_path.get(..prefix.len()).map(ByteSlice::as_bstr) != Some(prefix) + { + return None; + } + } + + let case = if ignore_case { Case::Fold } else { Case::Sensitive }; + let mut is_match = mapping.value.pattern.is_nil() || mapping.value.pattern.path.is_empty(); + if !is_match { + is_match = if mapping.pattern.first_wildcard_pos.is_none() { + match_verbatim(mapping, relative_path, is_dir, case) + } else { + let wildmatch_mode = match mapping.value.pattern.search_mode { + SearchMode::ShellGlob => Some(gix_glob::wildmatch::Mode::empty()), + SearchMode::Literal => None, + SearchMode::PathAwareGlob => Some(gix_glob::wildmatch::Mode::NO_MATCH_SLASH_LITERAL), + }; + match wildmatch_mode { + Some(wildmatch_mode) => { + let is_match = mapping.pattern.matches_repo_relative_path( + relative_path, + basename_not_important, + Some(is_dir), + case, + wildmatch_mode, + ); + if !is_match { + match_verbatim(mapping, relative_path, is_dir, case) + } else { + true + } + } + None => match_verbatim(mapping, relative_path, is_dir, case), + } + } + } + + if let Some(attrs) = mapping.value.attrs_match.as_mut() { + if !attributes(relative_path, Case::Sensitive, is_dir, attrs) { + // we have attrs, but it didn't match any + return None; + } + for (actual, expected) in attrs.iter_selected().zip(mapping.value.pattern.attributes.iter()) { + if actual.assignment != expected.as_ref() { + return None; + } + } + } + + is_match.then_some(Match { + pattern: &mapping.value.pattern, + sequence_number: mapping.sequence_number, + }) + }); + + if res.is_none() && self.all_patterns_are_excluded { + static MATCH_ALL_STAND_IN: Pattern = Pattern { + path: BString::new(Vec::new()), + signature: MagicSignature::empty(), + search_mode: SearchMode::ShellGlob, + attributes: Vec::new(), + prefix_len: 0, + nil: true, + }; + Some(Match { + pattern: &MATCH_ALL_STAND_IN, + sequence_number: patterns_len, + }) + } else { + res + } + } +} + +fn match_verbatim( + mapping: &gix_glob::search::pattern::Mapping<Spec>, + relative_path: &BStr, + is_dir: bool, + case: Case, +) -> bool { + let pattern_len = mapping.value.pattern.path.len(); + let mut relative_path_ends_with_slash_at_pattern_len = false; + let match_is_allowed = relative_path.get(pattern_len).map_or_else( + || relative_path.len() == pattern_len, + |b| { + relative_path_ends_with_slash_at_pattern_len = *b == b'/'; + relative_path_ends_with_slash_at_pattern_len + }, + ); + let pattern_requirement_is_met = !mapping.pattern.mode.contains(gix_glob::pattern::Mode::MUST_BE_DIR) + || (relative_path_ends_with_slash_at_pattern_len || is_dir); + + if match_is_allowed && pattern_requirement_is_met { + let dir_or_file = &relative_path[..mapping.value.pattern.path.len()]; + match case { + Case::Sensitive => mapping.value.pattern.path == dir_or_file, + Case::Fold => mapping.value.pattern.path.eq_ignore_ascii_case(dir_or_file), + } + } else { + false + } +} diff --git a/vendor/gix-pathspec/src/search/mod.rs b/vendor/gix-pathspec/src/search/mod.rs new file mode 100644 index 000000000..a9c87377b --- /dev/null +++ b/vendor/gix-pathspec/src/search/mod.rs @@ -0,0 +1,51 @@ +use bstr::{BStr, ByteSlice}; + +use crate::{Pattern, Search}; + +/// Describes a matching pattern within a search for ignored paths. +#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)] +pub struct Match<'a> { + /// The matching search specification, which contains the pathspec as well. + pub pattern: &'a Pattern, + /// The number of the sequence the matching pathspec was in, or the line of pathspec file it was read from if [Search::source] is not `None`. + pub sequence_number: usize, +} + +mod init; + +impl Match<'_> { + /// Return `true` if the pathspec that matched was negative, which excludes this item from the set. + pub fn is_excluded(&self) -> bool { + self.pattern.is_excluded() + } +} + +/// Access +impl Search { + /// Return an iterator over the patterns that participate in the search. + pub fn patterns(&self) -> impl Iterator<Item = &Pattern> + '_ { + self.patterns.iter().map(|m| &m.value.pattern) + } + + /// Return the portion of the prefix among all of the pathspecs involved in this search, or an empty string if + /// there is none. It doesn't have to end at a directory boundary though, nor does it denote a directory. + /// + /// Note that the common_prefix is always matched case-sensitively, and it is useful to skip large portions of input. + /// Further, excluded pathspecs don't participate which makes this common prefix inclusive. To work correclty though, + /// one will have to additionally match paths that have the common prefix with that pathspec itself to assure it is + /// not excluded. + pub fn common_prefix(&self) -> &BStr { + self.patterns + .iter() + .find(|p| !p.value.pattern.is_excluded()) + .map_or("".into(), |m| m.value.pattern.path[..self.common_prefix_len].as_bstr()) + } +} + +#[derive(Default, Clone, Debug)] +pub(crate) struct Spec { + pub pattern: Pattern, + pub attrs_match: Option<gix_attributes::search::Outcome>, +} + +mod matching; diff --git a/vendor/gix-pathspec/tests/defaults.rs b/vendor/gix-pathspec/tests/defaults.rs new file mode 100644 index 000000000..a30cbb7af --- /dev/null +++ b/vendor/gix-pathspec/tests/defaults.rs @@ -0,0 +1,95 @@ +use gix_pathspec::{Defaults, MagicSignature, SearchMode}; +use serial_test::serial; + +#[test] +#[serial] +fn literal_only_combines_with_icase() -> gix_testtools::Result { + { + let _env = gix_testtools::Env::new() + .set("GIT_LITERAL_PATHSPECS", "true") + .set("GIT_ICASE_PATHSPECS", "1") + .set("GIT_NOGLOB_PATHSPECS", "yes"); + assert_eq!( + Defaults::from_environment(&mut |n| std::env::var_os(n))?, + Defaults { + signature: MagicSignature::ICASE, + search_mode: SearchMode::Literal, + literal: true, + } + ); + } + { + let _env = gix_testtools::Env::new() + .set("GIT_LITERAL_PATHSPECS", "true") + .set("GIT_ICASE_PATHSPECS", "false") + .set("GIT_GLOB_PATHSPECS", "yes"); + assert_eq!( + Defaults::from_environment(&mut |n| std::env::var_os(n))?, + Defaults { + signature: MagicSignature::default(), + search_mode: SearchMode::Literal, + literal: true, + } + ); + } + Ok(()) +} +#[test] +#[serial] +fn nothing_is_set_then_it_is_like_the_default_impl() -> gix_testtools::Result { + assert_eq!( + Defaults::from_environment(&mut |n| std::env::var_os(n))?, + Defaults::default() + ); + Ok(()) +} + +#[test] +#[serial] +fn glob_and_noglob_cause_error() -> gix_testtools::Result { + let _env = gix_testtools::Env::new() + .set("GIT_GLOB_PATHSPECS", "1") + .set("GIT_NOGLOB_PATHSPECS", "yes"); + assert_eq!( + Defaults::from_environment(&mut |n| std::env::var_os(n)) + .unwrap_err() + .to_string(), + "Glob and no-glob settings are mutually exclusive" + ); + + Ok(()) +} + +#[test] +#[serial] +fn noglob_works() -> gix_testtools::Result { + let _env = gix_testtools::Env::new() + .set("GIT_GLOB_PATHSPECS", "0") + .set("GIT_NOGLOB_PATHSPECS", "true"); + assert_eq!( + Defaults::from_environment(&mut |n| std::env::var_os(n))?, + Defaults { + signature: MagicSignature::default(), + search_mode: SearchMode::Literal, + literal: false, + }, + "it's OK to set only one of them, and only no-glob has an interesting, non-default effect" + ); + Ok(()) +} + +#[test] +#[serial] +fn glob_works() -> gix_testtools::Result { + let _env = gix_testtools::Env::new().set("GIT_GLOB_PATHSPECS", "yes"); + assert_eq!( + Defaults::from_environment(&mut |n| std::env::var_os(n))?, + Defaults { + signature: MagicSignature::default(), + search_mode: SearchMode::PathAwareGlob, + literal: false, + }, + "docs here are strange but they mean to use pathaware globbing when this variable is set" + ); + Ok(()) +} diff --git a/vendor/gix-pathspec/tests/fixtures/generated-archives/match_baseline.tar.xz b/vendor/gix-pathspec/tests/fixtures/generated-archives/match_baseline.tar.xz Binary files differnew file mode 100644 index 000000000..2121b5579 --- /dev/null +++ b/vendor/gix-pathspec/tests/fixtures/generated-archives/match_baseline.tar.xz diff --git a/vendor/gix-pathspec/tests/fixtures/generated-archives/match_baseline_dirs.tar.xz b/vendor/gix-pathspec/tests/fixtures/generated-archives/match_baseline_dirs.tar.xz Binary files differnew file mode 100644 index 000000000..158b91b11 --- /dev/null +++ b/vendor/gix-pathspec/tests/fixtures/generated-archives/match_baseline_dirs.tar.xz diff --git a/vendor/gix-pathspec/tests/fixtures/generated-archives/match_baseline_files.tar.xz b/vendor/gix-pathspec/tests/fixtures/generated-archives/match_baseline_files.tar.xz Binary files differnew file mode 100644 index 000000000..3cd83f3fc --- /dev/null +++ b/vendor/gix-pathspec/tests/fixtures/generated-archives/match_baseline_files.tar.xz diff --git a/vendor/gix-pathspec/tests/fixtures/generated-archives/parse_baseline.tar.xz b/vendor/gix-pathspec/tests/fixtures/generated-archives/parse_baseline.tar.xz Binary files differnew file mode 100644 index 000000000..1b2ffb32c --- /dev/null +++ b/vendor/gix-pathspec/tests/fixtures/generated-archives/parse_baseline.tar.xz diff --git a/vendor/gix-pathspec/tests/fixtures/match_baseline_dirs.sh b/vendor/gix-pathspec/tests/fixtures/match_baseline_dirs.sh new file mode 100644 index 000000000..37af54e36 --- /dev/null +++ b/vendor/gix-pathspec/tests/fixtures/match_baseline_dirs.sh @@ -0,0 +1,97 @@ +#!/bin/bash +set -eu -o pipefail + +git init; +git init sub +(cd sub + : >empty + git add empty + git commit -m init-for-submodule +) + +git init parent +(cd parent + function baseline() { + local args="" + local specs="" + + for arg in "$@"; do + if [[ $arg == *"+"* ]]; then + echo "BUG: Argument '$arg' contains a space - we use that for separating pathspecs right now" >&2 + exit 1 + fi + args="$args -c submodule.active=$arg" + specs="${specs}+${arg}" + done + + { + echo "$specs" + git $args submodule + echo -n ';' + } >> baseline.git + } + + cat <<EOF >.gitattributes +bb bb-file +bb/ bb-dir +/bb/ bb-dir-from-top +EOF + + for p in a bb dir/b dir/bb dir/nested/c cc; do + git submodule add ../sub $p + git config --unset submodule.$p.active + done + git commit -m "init" + + git submodule > paths + + baseline ':' + baseline ':!' + baseline 'a' + baseline ':(attr:bb-file)' + # :(attr:bb-dir) - ["bb", "dir/bb"] == [] + baseline ':(attr:bb-dir)' git-inconsistency # bb/ matches recursively, git doesn't get it + # :(attr:bb-dir-from-top) - ["bb"] == [] + baseline ':(attr:bb-dir-from-top)' git-inconsistency # probably git doesn't really care about correctness here + baseline ':(icase)A' + baseline ':(icase,exclude)A' + baseline ':(icase,exclude)*/B*' + baseline ':(icase,exclude)*/B?' + baseline 'di' + baseline 'di?' + baseline 'di?/' + baseline 'dir*' + baseline 'dir/*' + baseline ':(glob)dir*' + baseline ':(glob,icase,exclude)dir*' + baseline ':(glob)dir/*' + baseline 'dir' + baseline 'dir/' + baseline ':(literal)dir' + baseline ':(literal)dir/' + baseline 'dir/nested' + baseline 'dir/nested/' + baseline ':(exclude)dir/' + baseline ':(icase)DIR' + baseline ':(icase)DIR/' + baseline ':!a' + baseline ':' ':!bb' + baseline ':!bb' + baseline 'a/' + baseline 'bb' + baseline 'dir/b' + baseline 'dir/b/' + # ["dir/b"] == [] + baseline '*/b/' git-inconsistency + baseline '*b' + baseline '*b*' + baseline '*b?' + baseline '*/b' + baseline '*/b?' + baseline '*/b*' + baseline '*c' + baseline '*/c' + baseline ':(glob)**/c' + baseline ':(glob)**/c?' + baseline ':(glob)**/c*' +) diff --git a/vendor/gix-pathspec/tests/fixtures/match_baseline_files.sh b/vendor/gix-pathspec/tests/fixtures/match_baseline_files.sh new file mode 100644 index 000000000..b8422ca11 --- /dev/null +++ b/vendor/gix-pathspec/tests/fixtures/match_baseline_files.sh @@ -0,0 +1,94 @@ +#!/bin/bash +set -eu -o pipefail + +git init; + +function baseline() { + local specs="" + for arg in "$@"; do + if [[ $arg == *"+"* ]]; then + echo "BUG: Argument '$arg' contains a space - we use that for separating pathspecs right now" >&2 + exit 1 + fi + specs="${specs}+${arg}" + done + + { + echo "$specs" + git ls-files "$@" + echo -n ';' + } >> baseline.git +} + +: >goo +: >'g[o][o]' +: >bar +echo 'goo a !b c=v -d' > .gitattributes +mkdir sub && :>sub/bar +git add . && git commit -m init +# this is to avoid issues on windows, which might not be able to manifest these files. +git -c core.protectNTFS=false update-index --add --cacheinfo 100644 "$(git rev-parse HEAD:goo)" "g*" +git update-index --add --cacheinfo 100644 "$(git rev-parse HEAD:goo)" "!a" +for p in bar bAr BAR foo/bar foo/bAr foo/BAR fOo/bar fOo/bAr fOo/BAR FOO/bar FOO/bAr FOO/BAR; do + git -c core.ignoreCase=false update-index --add --cacheinfo 100644 "$(git rev-parse HEAD:goo)" "$p" +done +git -c core.ignoreCase=false update-index --add --cacheinfo 100644 "$(git rev-parse HEAD:goo)" " " # 4 x space +git -c core.ignoreCase=false update-index --add --cacheinfo 100644 "$(git rev-parse HEAD:goo)" " hi " # 2 x space hi 2 x space + +git ls-files > paths + +baseline ':(attr:a)goo' +baseline ':(attr:a)Goo' +baseline ':(icase,attr:a)Goo' +baseline ':(attr:!b)goo' +baseline ':(attr:c=v)goo' +baseline ':(attr:-d)goo' +baseline ':(attr:a !b c=v -d)goo' +baseline ':(icase,attr:a !b c=v -d)GOO' +baseline ':(attr:a !b c=v -d)g*' +baseline ':(attr:none)goo' +baseline ':(literal)g*' +baseline 'sub/' +baseline 'sub' +baseline 'sub/*' +baseline 'sub*' +baseline ':(literal)g*' +baseline ':(glob)g*' +baseline ':(exclude,literal)g*' +baseline 'g*' +baseline ':(exclude)g*' +baseline ':(literal)?*' +baseline ':(exclude,literal)?*' +baseline '?*' +baseline ':(exclude)?*' +baseline 'g[o][o]' +# ["g[o][o]", "goo"] == ["g[o][o]"] +baseline ':(icase)G[O][o]' git-inconsistency +baseline ':(literal)g[o][o]' +baseline ':(literal,icase)G[o][O]' +baseline ':(glob)g[o][o]' +# ["g[o][o]", "goo"] == ["g[o][o]"] +baseline ':(glob,icase)g[o][O]' git-inconsistency +baseline ':(glob)**/bar' +baseline ':(literal)**/bar' +baseline '**/bar' +baseline '*/bar' +baseline ':(glob)*bar' +baseline ':(glob)**bar' +baseline '*bar' +baseline '*bar*' +baseline 'bar' +baseline 'bar/' +baseline 'sub/bar/' +baseline 'sub/bar' +baseline '!a' +baseline '?a' +baseline 'foo/' +baseline 'foo' +baseline 'foo/*' +baseline 'foo*' +baseline ':(icase)foo/' +baseline ':(icase)foo' +baseline ':(icase)foo/*' +baseline ':(icase)foo*' +baseline ':(icase)foo/bar' diff --git a/vendor/gix-pathspec/tests/fixtures/parse_baseline.sh b/vendor/gix-pathspec/tests/fixtures/parse_baseline.sh new file mode 100755 index 000000000..56525e9eb --- /dev/null +++ b/vendor/gix-pathspec/tests/fixtures/parse_baseline.sh @@ -0,0 +1,158 @@ +#!/bin/bash +set -eu -o pipefail + +git init; + +function baseline() { + local pathspec=$1 # first argument is the pathspec to test + + git ls-files "$pathspec" && status=0 || status=$? + { + echo "$pathspec" + echo "$status" + } >> baseline.git +} + +# success + +# special 'there is no pathspec' spec +baseline ':' + +# repeated_matcher_keywords +baseline ':(glob,glob)' +baseline ':(literal,literal)' +baseline ':(top,top)' +baseline ':(icase,icase)' +baseline ':(attr,attr)' +baseline ':!^(exclude,exclude)' + +# empty_signatures +baseline '.' +baseline ':' +baseline 'some/path' +baseline ':some/path' +baseline ':()some/path' +baseline '::some/path' +baseline ':::some/path' +baseline ':():some/path' + +# whitespace_in_pathspec +baseline ' some/path' +baseline 'some/ path' +baseline 'some/path ' +baseline ': some/path' +baseline ': !some/path' +baseline ': :some/path' +baseline ': ()some/path' +baseline ':! some/path' + +# short_signatures +baseline ':/some/path' +baseline '://some/path' +baseline ':^some/path' +baseline ':^^some/path' +baseline ':!some/path' +baseline ':!!some/path' +baseline ':/!some/path' +baseline ':!/^/:some/path' + +# signatures_and_searchmodes +baseline ':(top)' +baseline ':(icase)' +baseline ':(attr)' +baseline ':(exclude)' +baseline ':(literal)' +baseline ':(glob)' +baseline ':(top,exclude)' +baseline ':(icase,literal)' +baseline ':!(literal)some/*path' +baseline ':(top,literal,icase,attr,exclude)some/path' +baseline ':(top,glob,icase,attr,exclude)some/path' + +# attributes_in_signature +baseline ':(attr:someAttr)' +baseline ':(attr:!someAttr)' +baseline ':(attr:-someAttr)' +baseline ':(attr:someAttr=value)' +baseline ':(attr:a=one b=)' +baseline ':(attr:a= b=two)' +baseline ':(attr:a=one b=two)' +baseline ':(attr:a=one b=two)' +baseline ':(attr:someAttr anotherAttr)' + +# attributes_with_escape_chars_in_state_values +baseline ':(attr:v=one\-)' +baseline ':(attr:v=one\_)' +baseline ':(attr:v=one\,)' +baseline ':(attr:v=one\,two\,three)' +baseline ':(attr:a=\d b= c=\d)' + +# failing + +#empty_input +baseline "" + +# invalid_short_signatures +baseline ':"()' +baseline ':#()' +baseline ':%()' +baseline ':&()' +baseline ":'()" +baseline ':,()' +baseline ':-()' +baseline ':;()' +baseline ':<()' +baseline ':=()' +baseline ':>()' +baseline ':@()' +baseline ':_()' +baseline ':`()' +baseline ':~()' + +# invalid_keywords +baseline ':( )some/path' +baseline ':(tp)some/path' +baseline ':(top, exclude)some/path' +baseline ':(top,exclude,icse)some/path' + +# invalid_attributes +baseline ':(attr:+invalidAttr)some/path' +baseline ':(attr:validAttr +invalidAttr)some/path' +baseline ':(attr:+invalidAttr,attr:valid)some/path' +baseline ':(attr:inva\lid)some/path' + +# invalid_attribute_values +baseline ':(attr:v=inva#lid)some/path' +baseline ':(attr:v=inva\\lid)some/path' +baseline ':(attr:v=invalid\\)some/path' +baseline ':(attr:v=invalid\#)some/path' +baseline ':(attr:v=inva\=lid)some/path' +baseline ':(attr:a=valid b=inva\#lid)some/path' +baseline ':(attr:v=val��)' +baseline ':(attr:pr=pre��x:,)�' + +# escape_character_at_end_of_attribute_value +baseline ':(attr:v=invalid\)some/path' +baseline ':(attr:v=invalid\ )some/path' +baseline ':(attr:v=invalid\ valid)some/path' + +# empty_attribute_specification +baseline ':(attr:)' + +# multiple_attribute_specifications +baseline ':(attr:one,attr:two)some/path' + +# missing_parentheses +baseline ':(top' + +# glob_and_literal_keywords_present +baseline ':(glob,literal)some/path' +# trailing slash +baseline ':(glob,literal)some/path/' +baseline 'some/path/' +baseline 'path/' + +baseline 'a/b/' +baseline 'a/' +baseline '!a' +baseline '\!a' diff --git a/vendor/gix-pathspec/tests/normalize/mod.rs b/vendor/gix-pathspec/tests/normalize/mod.rs new file mode 100644 index 000000000..2fe54f664 --- /dev/null +++ b/vendor/gix-pathspec/tests/normalize/mod.rs @@ -0,0 +1,116 @@ +use std::path::Path; + +#[test] +fn removes_relative_path_components() -> crate::Result { + for (input_path, expected_path, expected_prefix) in [ + ("c", "a/b/c", "a/b"), + ("../c", "a/c", "a"), + ("../b/c", "a/b/c", "a"), // this is a feature - prefix components once consumed by .. are lost. Important as paths can contain globs + ("../*c/d", "a/*c/d", "a"), + ("../../c/d", "c/d", ""), + ("../../c/d/", "c/d", ""), + ("./c", "a/b/c", "a/b"), + ("../../c", "c", ""), + ("../..", ".", ""), + ("../././c", "a/c", "a"), + ("././/./c", "a/b/c", "a/b"), + ("././/./c/", "a/b/c", "a/b"), + ("././/./../c/d/", "a/c/d", "a"), + ] { + let spec = normalized_spec(input_path, "a/b", "")?; + assert_eq!(spec.path(), expected_path); + assert_eq!( + spec.prefix_directory(), + expected_prefix, + "{input_path} -> {expected_path}" + ); + } + Ok(()) +} + +#[test] +fn single_dot_is_special_and_directory_is_implied_without_trailing_slash() -> crate::Result { + for (input_path, expected) in [(".", "."), ("./", ".")] { + let spec = normalized_spec(input_path, "", "/repo")?; + assert_eq!(spec.path(), expected); + assert_eq!(spec.prefix_directory(), ""); + } + Ok(()) +} + +#[test] +fn absolute_path_made_relative() -> crate::Result { + for (input_path, expected, prefix_dir) in [ + ("/repo/a", "a", ""), + ("/repo/a/..//.///b", "b", ""), + ("/repo/a/", "a", "a"), + ("/repo/*/", "*", "*"), + ("/repo/a/b", "a/b", "a"), + ("/repo/*/b", "*/b", "*"), // we assume literal paths if specs are absolute + ("/repo/a/*/", "a/*", "a/*"), + ("/repo/a/b/", "a/b", "a/b"), + ("/repo/a/b/*", "a/b/*", "a/b"), + ("/repo/a/b/c/..", "a/b", "a"), + ] { + let spec = normalized_spec(input_path, "", "/repo")?; + assert_eq!(spec.path(), expected); + assert_eq!(spec.prefix_directory(), prefix_dir, "{input_path}"); + } + Ok(()) +} + +#[test] +fn relative_top_patterns_ignore_the_prefix() -> crate::Result { + let spec = normalized_spec(":(top)c", "a/b", "")?; + assert_eq!(spec.path(), "c"); + assert_eq!(spec.prefix_directory(), ""); + Ok(()) +} + +#[test] +fn absolute_top_patterns_ignore_the_prefix_but_are_made_relative() -> crate::Result { + let spec = normalized_spec(":(top)/a/b", "prefix-ignored", "/a")?; + assert_eq!(spec.path(), "b"); + assert_eq!(spec.prefix_directory(), ""); + Ok(()) +} + +#[test] +fn relative_path_breaks_out_of_working_tree() { + let err = normalized_spec("../a", "", "").unwrap_err(); + assert_eq!(err.to_string(), "The path '../a' leaves the repository"); + let err = normalized_spec("../../b", "a", "").unwrap_err(); + assert_eq!( + err.to_string(), + format!( + "The path '{}' leaves the repository", + if cfg!(windows) { "a\\../../b" } else { "a/../../b" } + ) + ); +} + +#[test] +fn absolute_path_breaks_out_of_working_tree() { + let err = normalized_spec("/path/to/repo/..///./a", "", "/path/to/repo").unwrap_err(); + assert_eq!(err.to_string(), "The path '..///./a' leaves the repository"); + let err = normalized_spec("/path/to/repo/../../../dev", "", "/path/to/repo").unwrap_err(); + assert_eq!(err.to_string(), "The path '../../../dev' leaves the repository"); +} + +#[test] +fn absolute_path_escapes_worktree() { + assert_eq!( + normalized_spec("/dev", "", "/path/to/repo").unwrap_err().to_string(), + "The path '/dev' is not inside of the worktree '/path/to/repo'" + ); +} + +fn normalized_spec( + path: &str, + prefix: &str, + root: &str, +) -> Result<gix_pathspec::Pattern, gix_pathspec::normalize::Error> { + let mut spec = gix_pathspec::parse(path.as_bytes(), Default::default()).expect("valid"); + spec.normalize(Path::new(prefix), Path::new(root))?; + Ok(spec) +} diff --git a/vendor/gix-pathspec/tests/parse/invalid.rs b/vendor/gix-pathspec/tests/parse/invalid.rs new file mode 100644 index 000000000..d080f4afc --- /dev/null +++ b/vendor/gix-pathspec/tests/parse/invalid.rs @@ -0,0 +1,152 @@ +use gix_pathspec::parse::Error; + +use crate::parse::check_against_baseline; + +#[test] +fn empty_input() { + let input = ""; + + assert!(!check_against_baseline(input), "This pathspec is valid in git: {input}"); + + let output = gix_pathspec::parse(input.as_bytes(), Default::default()); + assert!(output.is_err()); + assert!(matches!(output.unwrap_err(), Error::EmptyString)); +} + +#[test] +fn invalid_short_signatures() { + let inputs = vec![ + ":\"()", ":#()", ":%()", ":&()", ":'()", ":,()", ":-()", ":;()", ":<()", ":=()", ":>()", ":@()", ":_()", + ":`()", ":~()", + ]; + + for input in inputs.into_iter() { + assert!(!check_against_baseline(input), "This pathspec is valid in git: {input}"); + + let output = gix_pathspec::parse(input.as_bytes(), Default::default()); + assert!(output.is_err()); + assert!(matches!(output.unwrap_err(), Error::Unimplemented { .. })); + } +} + +#[test] +fn invalid_keywords() { + let inputs = vec![ + ":( )some/path", + ":(tp)some/path", + ":(top, exclude)some/path", + ":(top,exclude,icse)some/path", + ]; + + for input in inputs.into_iter() { + assert!(!check_against_baseline(input), "This pathspec is valid in git: {input}"); + + let output = gix_pathspec::parse(input.as_bytes(), Default::default()); + assert!(output.is_err()); + assert!(matches!(output.unwrap_err(), Error::InvalidKeyword { .. })); + } +} + +#[test] +fn invalid_attributes() { + let inputs = vec![ + ":(attr:+invalidAttr)some/path", + ":(attr:validAttr +invalidAttr)some/path", + ":(attr:+invalidAttr,attr:valid)some/path", + r":(attr:inva\lid)some/path", + ]; + + for input in inputs { + assert!(!check_against_baseline(input), "This pathspec is valid in git: {input}"); + + let output = gix_pathspec::parse(input.as_bytes(), Default::default()); + assert!(output.is_err(), "This pathspec did not produce an error {input}"); + assert!(matches!(output.unwrap_err(), Error::InvalidAttribute { .. })); + } +} + +#[test] +fn invalid_attribute_values() { + let inputs = vec![ + r":(attr:v=inva#lid)some/path", + r":(attr:v=inva\\lid)some/path", + r":(attr:v=invalid\\)some/path", + r":(attr:v=invalid\#)some/path", + r":(attr:v=inva\=lid)some/path", + r":(attr:a=valid b=inva\#lid)some/path", + ":(attr:v=val��)", + ":(attr:pr=pre��x:,)�", + ]; + + for input in inputs { + assert!(!check_against_baseline(input), "This pathspec is valid in git: {input}"); + + let output = gix_pathspec::parse(input.as_bytes(), Default::default()); + assert!(output.is_err(), "This pathspec did not produce an error {input}"); + assert!( + matches!(output.unwrap_err(), Error::InvalidAttributeValue { .. }), + "Errors did not match for pathspec: {input}" + ); + } +} + +#[test] +fn escape_character_at_end_of_attribute_value() { + let inputs = vec![ + r":(attr:v=invalid\)some/path", + r":(attr:v=invalid\ )some/path", + r":(attr:v=invalid\ valid)some/path", + ]; + + for input in inputs { + assert!(!check_against_baseline(input), "This pathspec is valid in git: {input}"); + + let output = gix_pathspec::parse(input.as_bytes(), Default::default()); + assert!(output.is_err(), "This pathspec did not produce an error {input}"); + assert!(matches!(output.unwrap_err(), Error::TrailingEscapeCharacter)); + } +} + +#[test] +fn empty_attribute_specification() { + let input = ":(attr:)"; + + assert!(!check_against_baseline(input), "This pathspec is valid in git: {input}"); + + let output = gix_pathspec::parse(input.as_bytes(), Default::default()); + assert!(output.is_err()); + assert!(matches!(output.unwrap_err(), Error::EmptyAttribute)); +} + +#[test] +fn multiple_attribute_specifications() { + let input = ":(attr:one,attr:two)some/path"; + + assert!(!check_against_baseline(input), "This pathspec is valid in git: {input}"); + + let output = gix_pathspec::parse(input.as_bytes(), Default::default()); + assert!(output.is_err()); + assert!(matches!(output.unwrap_err(), Error::MultipleAttributeSpecifications)); +} + +#[test] +fn missing_parentheses() { + let input = ":(top"; + + assert!(!check_against_baseline(input), "This pathspec is valid in git: {input}"); + + let output = gix_pathspec::parse(input.as_bytes(), Default::default()); + assert!(output.is_err()); + assert!(matches!(output.unwrap_err(), Error::MissingClosingParenthesis { .. })); +} + +#[test] +fn glob_and_literal_keywords_present() { + let input = ":(glob,literal)some/path"; + + assert!(!check_against_baseline(input), "This pathspec is valid in git: {input}"); + + let output = gix_pathspec::parse(input.as_bytes(), Default::default()); + assert!(output.is_err()); + assert!(matches!(output.unwrap_err(), Error::IncompatibleSearchModes)); +} diff --git a/vendor/gix-pathspec/tests/parse/mod.rs b/vendor/gix-pathspec/tests/parse/mod.rs new file mode 100644 index 000000000..6ee363d6b --- /dev/null +++ b/vendor/gix-pathspec/tests/parse/mod.rs @@ -0,0 +1,93 @@ +use std::collections::HashMap; + +use bstr::{BStr, BString, ByteSlice}; +use gix_attributes::State; +use gix_pathspec::{MagicSignature, Pattern, SearchMode}; +use once_cell::sync::Lazy; + +#[test] +fn baseline() { + for (pattern, exit_code) in BASELINE.iter() { + let res = gix_pathspec::parse(pattern, Default::default()); + assert_eq!( + res.is_ok(), + *exit_code == 0, + "{pattern:?} disagrees with baseline: {res:?}" + ); + if let Ok(pat) = res { + let actual = pat.to_bstring(); + assert_eq!( + pat, + gix_pathspec::parse(actual.as_ref(), Default::default()).expect("still valid"), + "{pattern} != {actual}: display must roundtrip into actual pattern" + ); + } + let p = gix_pathspec::Pattern::from_literal(pattern, Default::default()); + assert!(matches!(p.search_mode, SearchMode::Literal)); + } +} + +mod invalid; +mod valid; + +/// A way to specify expectations more easily by simplifying assignments. +#[derive(Debug, Clone, PartialEq, Eq)] +struct NormalizedPattern { + path: BString, + signature: MagicSignature, + search_mode: SearchMode, + attributes: Vec<(BString, State)>, +} + +impl From<Pattern> for NormalizedPattern { + fn from(p: Pattern) -> Self { + NormalizedPattern { + path: p.path().to_owned(), + signature: p.signature, + search_mode: p.search_mode, + attributes: p + .attributes + .into_iter() + .map(|attr| (attr.name.as_str().into(), attr.state)) + .collect(), + } + } +} + +static BASELINE: Lazy<HashMap<BString, usize>> = Lazy::new(|| { + let base = gix_testtools::scripted_fixture_read_only("parse_baseline.sh").unwrap(); + + (|| -> crate::Result<_> { + let mut map = HashMap::new(); + let baseline = std::fs::read(base.join("baseline.git"))?; + let mut lines = baseline.lines(); + while let Some(spec) = lines.next() { + let exit_code = lines.next().expect("two lines per baseline").to_str()?.parse()?; + map.insert(spec.into(), exit_code); + } + Ok(map) + })() + .unwrap() +}); + +fn check_valid_inputs<'a>(inputs: impl IntoIterator<Item = (&'a str, NormalizedPattern)>) { + for (input, expected) in inputs.into_iter() { + assert!( + check_against_baseline(input), + "This pathspec is invalid in git: {input}" + ); + + let pattern = gix_pathspec::parse(input.as_bytes(), Default::default()) + .unwrap_or_else(|_| panic!("parsing should not fail with pathspec {input}")); + let pattern: NormalizedPattern = pattern.into(); + assert_eq!(pattern, expected, "while checking input: \"{input}\""); + } +} + +fn check_against_baseline(pathspec: &str) -> bool { + let key: &BStr = pathspec.into(); + let base = BASELINE + .get(key) + .unwrap_or_else(|| panic!("missing baseline for pathspec: {pathspec:?}")); + *base == 0 +} diff --git a/vendor/gix-pathspec/tests/parse/valid.rs b/vendor/gix-pathspec/tests/parse/valid.rs new file mode 100644 index 000000000..ce0b05d88 --- /dev/null +++ b/vendor/gix-pathspec/tests/parse/valid.rs @@ -0,0 +1,359 @@ +use gix_attributes::State; +use gix_pathspec::{MagicSignature, SearchMode}; + +use crate::parse::{check_against_baseline, check_valid_inputs, NormalizedPattern}; + +#[test] +fn repeated_matcher_keywords() { + let input = vec![ + (":(glob,glob)", pat_with_search_mode(SearchMode::PathAwareGlob)), + (":(literal,literal)", pat_with_search_mode(SearchMode::Literal)), + (":(top,top)", pat_with_sig(MagicSignature::TOP)), + (":(icase,icase)", pat_with_sig(MagicSignature::ICASE)), + (":(attr,attr)", pat_with_attrs(vec![])), + (":!^(exclude,exclude)", pat_with_sig(MagicSignature::EXCLUDE)), + ]; + + check_valid_inputs(input); +} + +#[test] +fn glob_negations_are_always_literal() { + check_valid_inputs([("!a", pat_with_path("!a")), ("\\!a", pat_with_path("\\!a"))]); +} + +#[test] +fn literal_default_prevents_parsing() { + let pattern = gix_pathspec::parse( + ":".as_bytes(), + gix_pathspec::Defaults { + signature: MagicSignature::EXCLUDE, + search_mode: SearchMode::PathAwareGlob, + literal: true, + }, + ) + .expect("valid"); + assert!(!pattern.is_nil()); + assert_eq!(pattern.path(), ":"); + assert!(matches!(pattern.search_mode, SearchMode::Literal)); + + let input = ":(literal)f[o][o]"; + let pattern = gix_pathspec::parse( + input.as_bytes(), + gix_pathspec::Defaults { + signature: MagicSignature::TOP, + search_mode: SearchMode::Literal, + literal: true, + }, + ) + .expect("valid"); + assert_eq!(pattern.path(), input, "no parsing happens at all"); + assert!(matches!(pattern.search_mode, SearchMode::Literal)); + + let pattern = gix_pathspec::parse( + input.as_bytes(), + gix_pathspec::Defaults { + signature: MagicSignature::TOP, + search_mode: SearchMode::Literal, + literal: false, + }, + ) + .expect("valid"); + assert_eq!(pattern.path(), "f[o][o]", "in literal default mode, we still parse"); + assert!(matches!(pattern.search_mode, SearchMode::Literal)); +} + +#[test] +fn there_is_no_pathspec_pathspec() { + check_against_baseline(":"); + let pattern = gix_pathspec::parse(":".as_bytes(), Default::default()).expect("valid"); + assert!(pattern.is_nil()); + + let actual: NormalizedPattern = pattern.into(); + assert_eq!(actual, pat_with_path("")); + + let pattern = gix_pathspec::parse( + ":".as_bytes(), + gix_pathspec::Defaults { + signature: MagicSignature::EXCLUDE, + search_mode: SearchMode::PathAwareGlob, + literal: false, + }, + ) + .expect("valid"); + assert!(pattern.is_nil()); +} + +#[test] +fn defaults_are_used() -> crate::Result { + let defaults = gix_pathspec::Defaults { + signature: MagicSignature::EXCLUDE, + search_mode: SearchMode::Literal, + literal: false, + }; + let p = gix_pathspec::parse(".".as_bytes(), defaults)?; + assert_eq!(p.path(), "."); + assert_eq!(p.signature, defaults.signature); + assert_eq!(p.search_mode, defaults.search_mode); + assert!(p.attributes.is_empty()); + assert!(!p.is_nil()); + Ok(()) +} + +#[test] +fn literal_from_defaults_is_overridden_by_element_glob() -> crate::Result { + let defaults = gix_pathspec::Defaults { + search_mode: SearchMode::Literal, + ..Default::default() + }; + let p = gix_pathspec::parse(":(glob)*override".as_bytes(), defaults)?; + assert_eq!(p.path(), "*override"); + assert_eq!(p.signature, MagicSignature::default()); + assert_eq!(p.search_mode, SearchMode::PathAwareGlob, "this is the element override"); + assert!(p.attributes.is_empty()); + assert!(!p.is_nil()); + Ok(()) +} + +#[test] +fn glob_from_defaults_is_overridden_by_element_glob() -> crate::Result { + let defaults = gix_pathspec::Defaults { + search_mode: SearchMode::PathAwareGlob, + ..Default::default() + }; + let p = gix_pathspec::parse(":(literal)*override".as_bytes(), defaults)?; + assert_eq!(p.path(), "*override"); + assert_eq!(p.signature, MagicSignature::default()); + assert_eq!(p.search_mode, SearchMode::Literal, "this is the element override"); + assert!(p.attributes.is_empty()); + assert!(!p.is_nil()); + Ok(()) +} + +#[test] +fn empty_signatures() { + let inputs = vec![ + (".", pat_with_path(".")), + ("some/path", pat_with_path("some/path")), + (":some/path", pat_with_path("some/path")), + (":()some/path", pat_with_path("some/path")), + ("::some/path", pat_with_path("some/path")), + (":::some/path", pat_with_path(":some/path")), + (":():some/path", pat_with_path(":some/path")), + ]; + + check_valid_inputs(inputs) +} + +#[test] +fn whitespace_in_pathspec() { + let inputs = vec![ + (" some/path", pat_with_path(" some/path")), + ("some/ path", pat_with_path("some/ path")), + ("some/path ", pat_with_path("some/path ")), + (": some/path", pat_with_path(" some/path")), + (": !some/path", pat_with_path(" !some/path")), + (": :some/path", pat_with_path(" :some/path")), + (": ()some/path", pat_with_path(" ()some/path")), + ( + ":! some/path", + pat_with_path_and_sig(" some/path", MagicSignature::EXCLUDE), + ), + ( + ":!!some/path", + pat_with_path_and_sig("some/path", MagicSignature::EXCLUDE), + ), + ]; + + check_valid_inputs(inputs) +} + +#[test] +fn short_signatures() { + let inputs = vec![ + (":/some/path", pat_with_path_and_sig("some/path", MagicSignature::TOP)), + ( + ":^some/path", + pat_with_path_and_sig("some/path", MagicSignature::EXCLUDE), + ), + ( + ":!some/path", + pat_with_path_and_sig("some/path", MagicSignature::EXCLUDE), + ), + ( + ":/!some/path", + pat_with_path_and_sig("some/path", MagicSignature::TOP | MagicSignature::EXCLUDE), + ), + ( + ":!/^/:some/path", + pat_with_path_and_sig("some/path", MagicSignature::TOP | MagicSignature::EXCLUDE), + ), + ]; + + check_valid_inputs(inputs) +} + +#[test] +fn trailing_slash_is_turned_into_magic_signature_and_removed() { + check_valid_inputs([ + ("a/b/", pat_with_path_and_sig("a/b", MagicSignature::MUST_BE_DIR)), + ("a/", pat_with_path_and_sig("a", MagicSignature::MUST_BE_DIR)), + ]); +} + +#[test] +fn signatures_and_searchmodes() { + let inputs = vec![ + (":(top)", pat_with_sig(MagicSignature::TOP)), + (":(icase)", pat_with_sig(MagicSignature::ICASE)), + (":(attr)", pat_with_path("")), + (":(exclude)", pat_with_sig(MagicSignature::EXCLUDE)), + (":(literal)", pat_with_search_mode(SearchMode::Literal)), + (":(glob)", pat_with_search_mode(SearchMode::PathAwareGlob)), + ( + ":(top,exclude)", + pat_with_sig(MagicSignature::TOP | MagicSignature::EXCLUDE), + ), + ( + ":(icase,literal)", + pat("", MagicSignature::ICASE, SearchMode::Literal, vec![]), + ), + ( + ":!(literal)some/*path", + pat("some/*path", MagicSignature::EXCLUDE, SearchMode::Literal, vec![]), + ), + ( + ":(top,literal,icase,attr,exclude)some/path", + pat( + "some/path", + MagicSignature::TOP | MagicSignature::EXCLUDE | MagicSignature::ICASE, + SearchMode::Literal, + vec![], + ), + ), + ( + ":(top,glob,icase,attr,exclude)some/path", + pat( + "some/path", + MagicSignature::TOP | MagicSignature::EXCLUDE | MagicSignature::ICASE, + SearchMode::PathAwareGlob, + vec![], + ), + ), + ]; + + check_valid_inputs(inputs); +} + +#[test] +fn attributes_in_signature() { + let inputs = vec![ + (":(attr:someAttr)", pat_with_attrs(vec![("someAttr", State::Set)])), + ( + ":(attr:!someAttr)", + pat_with_attrs(vec![("someAttr", State::Unspecified)]), + ), + (":(attr:-someAttr)", pat_with_attrs(vec![("someAttr", State::Unset)])), + ( + ":(attr:someAttr=value)", + pat_with_attrs(vec![("someAttr", State::Value("value".into()))]), + ), + ( + ":(attr:a=one b=)", + pat_with_attrs(vec![("a", State::Value("one".into())), ("b", State::Value("".into()))]), + ), + ( + ":(attr:a= b=two)", + pat_with_attrs(vec![("a", State::Value("".into())), ("b", State::Value("two".into()))]), + ), + ( + ":(attr:a=one b=two)", + pat_with_attrs(vec![ + ("a", State::Value("one".into())), + ("b", State::Value("two".into())), + ]), + ), + ( + ":(attr:a=one b=two)", + pat_with_attrs(vec![ + ("a", State::Value("one".into())), + ("b", State::Value("two".into())), + ]), + ), + ( + ":(attr:someAttr anotherAttr)", + pat_with_attrs(vec![("someAttr", State::Set), ("anotherAttr", State::Set)]), + ), + ]; + + check_valid_inputs(inputs) +} + +#[test] +fn attributes_with_escape_chars_in_state_values() { + let inputs = vec![ + ( + r":(attr:v=one\-)", + pat_with_attrs(vec![("v", State::Value(r"one-".into()))]), + ), + ( + r":(attr:v=one\_)", + pat_with_attrs(vec![("v", State::Value(r"one_".into()))]), + ), + ( + r":(attr:v=one\,)", + pat_with_attrs(vec![("v", State::Value(r"one,".into()))]), + ), + ( + r":(attr:v=one\,two\,three)", + pat_with_attrs(vec![("v", State::Value(r"one,two,three".into()))]), + ), + ( + r":(attr:a=\d b= c=\d)", + pat_with_attrs(vec![ + ("a", State::Value(r"d".into())), + ("b", State::Value(r"".into())), + ("c", State::Value(r"d".into())), + ]), + ), + ]; + + check_valid_inputs(inputs) +} + +fn pat_with_path(path: &str) -> NormalizedPattern { + pat_with_path_and_sig(path, MagicSignature::empty()) +} + +fn pat_with_path_and_sig(path: &str, signature: MagicSignature) -> NormalizedPattern { + pat(path, signature, SearchMode::ShellGlob, vec![]) +} + +fn pat_with_sig(signature: MagicSignature) -> NormalizedPattern { + pat("", signature, SearchMode::ShellGlob, vec![]) +} + +fn pat_with_attrs(attrs: Vec<(&'static str, State)>) -> NormalizedPattern { + pat("", MagicSignature::empty(), SearchMode::ShellGlob, attrs) +} + +fn pat_with_search_mode(search_mode: SearchMode) -> NormalizedPattern { + pat("", MagicSignature::empty(), search_mode, vec![]) +} + +fn pat( + path: &str, + signature: MagicSignature, + search_mode: SearchMode, + attributes: Vec<(&str, State)>, +) -> NormalizedPattern { + NormalizedPattern { + path: path.into(), + signature, + search_mode, + attributes: attributes + .into_iter() + .map(|(attr, state)| (attr.into(), state)) + .collect(), + } +} diff --git a/vendor/gix-pathspec/tests/pathspec.rs b/vendor/gix-pathspec/tests/pathspec.rs new file mode 100644 index 000000000..d83fefbcb --- /dev/null +++ b/vendor/gix-pathspec/tests/pathspec.rs @@ -0,0 +1,5 @@ +pub use gix_testtools::Result; + +mod normalize; +mod parse; +mod search; diff --git a/vendor/gix-pathspec/tests/search/mod.rs b/vendor/gix-pathspec/tests/search/mod.rs new file mode 100644 index 000000000..638709c35 --- /dev/null +++ b/vendor/gix-pathspec/tests/search/mod.rs @@ -0,0 +1,281 @@ +use std::path::Path; + +#[test] +fn directories() -> crate::Result { + baseline::run("directory", true, baseline::directories) +} + +#[test] +fn no_pathspecs_match_everything() -> crate::Result { + let mut search = gix_pathspec::Search::from_specs([], None, Path::new(""))?; + assert_eq!(search.patterns().count(), 0, "nothing artificial is added"); + let m = search + .pattern_matching_relative_path("hello".into(), None, &mut |_, _, _, _| { + unreachable!("must not be called") + }) + .expect("matches"); + assert_eq!(m.pattern.prefix_directory(), "", "there is no prefix as none was given"); + + Ok(()) +} + +#[test] +fn init_with_exclude() -> crate::Result { + let search = gix_pathspec::Search::from_specs(pathspecs(&["tests/", ":!*.sh"]), None, Path::new(""))?; + assert_eq!(search.patterns().count(), 2, "nothing artificial is added"); + assert!( + search.patterns().next().expect("first of two").is_excluded(), + "re-orded so that excluded are first" + ); + assert_eq!(search.common_prefix(), "tests"); + Ok(()) +} + +#[test] +fn no_pathspecs_respect_prefix() -> crate::Result { + let mut search = gix_pathspec::Search::from_specs([], Some(Path::new("a")), Path::new(""))?; + assert_eq!( + search.patterns().count(), + 1, + "we get an artificial pattern to get the prefix" + ); + assert!( + search + .pattern_matching_relative_path("hello".into(), None, &mut |_, _, _, _| unreachable!( + "must not be called" + )) + .is_none(), + "not the right prefix" + ); + let m = search + .pattern_matching_relative_path("a/b".into(), None, &mut |_, _, _, _| unreachable!("must not be called")) + .expect("match"); + assert_eq!( + m.pattern.prefix_directory(), + "a", + "the prefix directory matched verbatim" + ); + + Ok(()) +} + +#[test] +fn prefixes_are_always_case_insensitive() -> crate::Result { + let path = gix_testtools::scripted_fixture_read_only("match_baseline_files.sh")?.join("paths"); + let items = baseline::parse_paths(path)?; + + for (spec, prefix, common_prefix, expected) in [ + (":(icase)bar", "FOO", "FOO", &["FOO/BAR", "FOO/bAr", "FOO/bar"] as &[_]), + (":(icase)bar", "F", "F", &[]), + (":(icase)bar", "FO", "FO", &[]), + (":(icase)../bar", "fOo", "", &["BAR", "bAr", "bar"]), + ("../bar", "fOo", "bar", &["bar"]), + (" ", "", " ", &[" "]), // whitespace can match verbatim + (" hi*", "", " hi", &[" hi "]), // whitespace can match with globs as well + (":(icase)../bar", "fO", "", &["BAR", "bAr", "bar"]), // prefixes are virtual, and don't have to exist at all. + ( + ":(icase)../foo/bar", + "FOO", + "", + &[ + "FOO/BAR", "FOO/bAr", "FOO/bar", "fOo/BAR", "fOo/bAr", "fOo/bar", "foo/BAR", "foo/bAr", "foo/bar", + ], + ), + ("../foo/bar", "FOO", "foo/bar", &["foo/bar"]), + ( + ":(icase)../foo/../fOo/bar", + "FOO", + "", + &[ + "FOO/BAR", "FOO/bAr", "FOO/bar", "fOo/BAR", "fOo/bAr", "fOo/bar", "foo/BAR", "foo/bAr", "foo/bar", + ], + ), + ("../foo/../fOo/BAR", "FOO", "fOo/BAR", &["fOo/BAR"]), + ] { + let mut search = gix_pathspec::Search::from_specs( + gix_pathspec::parse(spec.as_bytes(), Default::default()), + Some(Path::new(prefix)), + Path::new(""), + )?; + assert_eq!(search.common_prefix(), common_prefix, "{spec} {prefix}"); + let actual: Vec<_> = items + .iter() + .filter(|relative_path| { + search + .pattern_matching_relative_path(relative_path.as_str().into(), Some(false), &mut |_, _, _, _| false) + .is_some() + }) + .collect(); + assert_eq!(actual, expected, "{spec} {prefix}"); + } + Ok(()) +} + +#[test] +fn common_prefix() -> crate::Result { + for (specs, prefix, expected) in [ + (&["foo/bar", ":(icase)foo/bar"] as &[_], None, ""), + (&["foo/bar", "foo"], None, "foo"), + (&["foo/bar/baz", "foo/bar/"], None, "foo/bar"), // directory trailing slashes are ignored, but that prefix shouldn't care anyway + (&[":(icase)bar", ":(icase)bart"], Some("foo"), "foo"), // only case-sensitive portions count + (&["bar", "bart"], Some("foo"), "foo/bar"), // otherwise everything that matches counts + (&["bar", "bart", "ba"], Some("foo"), "foo/ba"), + ] { + let search = gix_pathspec::Search::from_specs( + specs + .iter() + .map(|s| gix_pathspec::parse(s.as_bytes(), Default::default()).expect("valid")), + prefix.map(Path::new), + Path::new(""), + )?; + assert_eq!(search.common_prefix(), expected, "{specs:?} {prefix:?}"); + } + Ok(()) +} + +#[test] +fn files() -> crate::Result { + baseline::run("file", false, baseline::files) +} + +fn pathspecs(input: &[&str]) -> Vec<gix_pathspec::Pattern> { + input + .iter() + .map(|pattern| gix_pathspec::parse(pattern.as_bytes(), Default::default()).expect("known to be valid")) + .collect() +} + +mod baseline { + use std::path::{Path, PathBuf}; + + use bstr::{BString, ByteSlice}; + + pub fn run( + name: &str, + items_are_dirs: bool, + init: impl FnOnce() -> crate::Result<(PathBuf, Vec<String>, Vec<Expected>)>, + ) -> crate::Result { + let (root, items, expected) = init()?; + let mut collection = Default::default(); + let attrs = + gix_attributes::Search::new_globals(Some(root.join(".gitattributes")), &mut Vec::new(), &mut collection)?; + let tests = expected.len(); + for expected in expected { + let mut search = gix_pathspec::Search::from_specs(expected.pathspecs, None, Path::new(""))?; + let actual: Vec<_> = items + .iter() + .filter(|path| { + search + .pattern_matching_relative_path( + path.as_str().into(), + Some(items_are_dirs), + &mut |rela_path, case, is_dir, out| { + out.initialize(&collection); + attrs.pattern_matching_relative_path(rela_path, case, Some(is_dir), out) + }, + ) + .map_or(false, |m| !m.is_excluded()) + }) + .cloned() + .collect(); + let matches_expectation = actual == expected.matches; + assert_eq!( + matches_expectation, + expected.is_consistent, + "{} - {actual:?} == {:?}", + search.patterns().map(|p| format!("{p}")).collect::<Vec<_>>().join(", "), + expected.matches + ); + } + eprintln!("{tests} {name} matches OK"); + Ok(()) + } + + #[derive(Debug)] + pub struct Expected { + pub pathspecs: Vec<gix_pathspec::Pattern>, + pub matches: Vec<String>, + /// If true, this means that the baseline is different from what we get, and that our solution is consistent with the rules. + pub is_consistent: bool, + } + + pub fn parse_paths(path: PathBuf) -> std::io::Result<Vec<String>> { + let buf = std::fs::read(path)?; + Ok(buf.lines().map(BString::from).map(|s| s.to_string()).collect()) + } + + fn parse_blocks(input: &[u8], parse_matches: impl Fn(&[u8]) -> Vec<String>) -> Vec<Expected> { + input + .split(|b| *b == b';') + .filter(|b| !b.is_empty()) + .map(move |block| { + let mut lines = block.lines(); + let mut is_inconsistent = false; + let pathspecs = lines + .next() + .expect("pathspec") + .split(|b| *b == b'+') + .filter(|spec| { + is_inconsistent = spec.as_bstr() == "git-inconsistency"; + !is_inconsistent + }) + .filter_map(|s| (!s.trim().is_empty()).then(|| s.trim())) + .map(|pathspec| gix_pathspec::parse(pathspec, Default::default()).expect("valid pathspec")) + .collect(); + Expected { + pathspecs, + matches: parse_matches(lines.as_bytes()), + is_consistent: !is_inconsistent, + } + }) + .collect() + } + + mod submodule { + use bstr::ByteSlice; + + pub fn matches_from_status(input: &[u8]) -> impl Iterator<Item = (bool, String)> + '_ { + input.lines().map(|line| { + let matches = line[0] == b' '; + assert_eq!(!matches, line[0] == b'-'); + let mut tokens = line[1..].split(|b| *b == b' ').skip(1); + let path = tokens.next().expect("path").to_str().expect("valid UTF-8"); + (matches, path.to_owned()) + }) + } + + pub fn parse_expected(input: &[u8]) -> Vec<super::Expected> { + super::parse_blocks(input, |block| { + matches_from_status(block) + .filter_map(|(matches, module_path)| matches.then_some(module_path)) + .collect() + }) + } + } + + mod files { + use bstr::{BString, ByteSlice}; + pub fn parse_expected(input: &[u8]) -> Vec<super::Expected> { + super::parse_blocks(input, |block| { + block.lines().map(BString::from).map(|s| s.to_string()).collect() + }) + } + } + + pub fn directories() -> crate::Result<(PathBuf, Vec<String>, Vec<Expected>)> { + let root = gix_testtools::scripted_fixture_read_only("match_baseline_dirs.sh")?.join("parent"); + let buf = std::fs::read(root.join("paths"))?; + let items = submodule::matches_from_status(&buf) + .map(|(_matches, path)| path) + .collect(); + let expected = submodule::parse_expected(&std::fs::read(root.join("baseline.git"))?); + Ok((root, items, expected)) + } + + pub fn files() -> crate::Result<(PathBuf, Vec<String>, Vec<Expected>)> { + let root = gix_testtools::scripted_fixture_read_only("match_baseline_files.sh")?; + let items = parse_paths(root.join("paths"))?; + let expected = files::parse_expected(&std::fs::read(root.join("baseline.git"))?); + Ok((root, items, expected)) + } +} |