Use gix for cargo package (#15534)

This should also help fixing these spurious "cannot package because some
excluded file is untracked" issues.

### Tasks

* [x] step-by-step conversion of `vcs.rs`
* [x] use proper feature toggle
* [x] ~~cleanup~~ final check by myself
* [ ] ~~move split & rename into its own commit. Probably squash all
changes except for the gix upgrade.~~
     - I like to have the major stages of this PR conserved.
* [x] upgrade to a gix release including
https://github.com/GitoxideLabs/gitoxide/pull/2016
     - This was done in `master` already.
* [x] fix tests by fixing `gix` - `submodules()` call isn't bare-repo
safe.
* [x] fix failure on Windows
- `gix status` seems to go through a symlink, arriving at the wrong
conclusion, on Windows.
* [x] fix performance regression on `aws-sdk-rust`.

### Notes for the Reviewer

* This implementation is both faster and more correct, thus affects
#15416 and #14955.

Related to https://github.com/GitoxideLabs/gitoxide/issues/106.
This commit is contained in:
Weihang Lo 2025-07-23 03:13:49 +00:00 committed by GitHub
commit 9b5231f892
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 383 additions and 243 deletions

266
Cargo.lock generated
View File

@ -8,18 +8,6 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "ahash"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "1.1.3"
@ -904,6 +892,20 @@ dependencies = [
"syn 2.0.104",
]
[[package]]
name = "dashmap"
version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
dependencies = [
"cfg-if",
"crossbeam-utils",
"hashbrown 0.14.5",
"lock_api",
"once_cell",
"parking_lot_core",
]
[[package]]
name = "der"
version = "0.7.10"
@ -1258,9 +1260,9 @@ dependencies = [
[[package]]
name = "gix"
version = "0.72.1"
version = "0.73.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01237e8d3d78581f71642be8b0c2ae8c0b2b5c251c9c5d9ebbea3c1ea280dce8"
checksum = "514c29cc879bdc0286b0cbc205585a49b252809eb86c69df4ce4f855ee75f635"
dependencies = [
"gix-actor",
"gix-attributes",
@ -1295,6 +1297,7 @@ dependencies = [
"gix-revwalk",
"gix-sec",
"gix-shallow",
"gix-status",
"gix-submodule",
"gix-tempfile",
"gix-trace",
@ -1312,9 +1315,9 @@ dependencies = [
[[package]]
name = "gix-actor"
version = "0.35.1"
version = "0.35.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b300e6e4f31f3f6bd2de5e2b0caab192ced00dc0fcd0f7cc56e28c575c8e1ff"
checksum = "58ebbb8f41071c7cf318a0b1db667c34e1df49db7bf387d282a4e61a3b97882c"
dependencies = [
"bstr",
"gix-date",
@ -1326,9 +1329,9 @@ dependencies = [
[[package]]
name = "gix-attributes"
version = "0.26.1"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f50d813d5c2ce9463ba0c29eea90060df08e38ad8f34b8a192259f8bce5c078"
checksum = "45442188216d08a5959af195f659cb1f244a50d7d2d0c3873633b1cd7135f638"
dependencies = [
"bstr",
"gix-glob",
@ -1361,9 +1364,9 @@ dependencies = [
[[package]]
name = "gix-command"
version = "0.6.1"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d05dd813ef6bb798570308aa7f1245cefa350ec9f30dc53308335eb22b9d0f8b"
checksum = "6b31b65ca48a352ae86312b27a514a0c661935f96b481ac8b4371f65815eb196"
dependencies = [
"bstr",
"gix-path",
@ -1374,9 +1377,9 @@ dependencies = [
[[package]]
name = "gix-commitgraph"
version = "0.28.0"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e05050fd6caa6c731fe3bd7f9485b3b520be062d3d139cb2626e052d6c127951"
checksum = "6bb23121e952f43a5b07e3e80890336cb847297467a410475036242732980d06"
dependencies = [
"bstr",
"gix-chunk",
@ -1387,9 +1390,9 @@ dependencies = [
[[package]]
name = "gix-config"
version = "0.45.1"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48f3c8f357ae049bfb77493c2ec9010f58cfc924ae485e1116c3718fc0f0d881"
checksum = "5dfb898c5b695fd4acfc3c0ab638525a65545d47706064dcf7b5ead6cdb136c0"
dependencies = [
"bstr",
"gix-config-value",
@ -1408,9 +1411,9 @@ dependencies = [
[[package]]
name = "gix-config-value"
version = "0.15.0"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439d62e241dae2dffd55bfeeabe551275cf9d9f084c5ebc6b48bad49d03285b7"
checksum = "9f012703eb67e263c6c1fc96649fec47694dd3e5d2a91abfc65e4a6a6dc85309"
dependencies = [
"bitflags",
"bstr",
@ -1421,13 +1424,14 @@ dependencies = [
[[package]]
name = "gix-credentials"
version = "0.29.0"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce1c7307e36026b6088e5b12014ffe6d4f509c911ee453e22a7be4003a159c9b"
checksum = "0039dd3ac606dd80b16353a41b61fc237ca5cb8b612f67a9f880adfad4be4e05"
dependencies = [
"bstr",
"gix-command",
"gix-config-value",
"gix-date",
"gix-path",
"gix-prompt",
"gix-sec",
@ -1438,9 +1442,9 @@ dependencies = [
[[package]]
name = "gix-date"
version = "0.10.2"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "139d1d52b21741e3f0c72b0fc65e1ff34d4eaceb100ef529d182725d2e09b8cb"
checksum = "d7235bdf4d9d54a6901928e3a37f91c16f419e6957f520ed929c3d292b84226e"
dependencies = [
"bstr",
"itoa 1.0.15",
@ -1451,21 +1455,33 @@ dependencies = [
[[package]]
name = "gix-diff"
version = "0.52.1"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e9b43e95fe352da82a969f0c84ff860c2de3e724d93f6681fedbcd6c917f252"
checksum = "de854852010d44a317f30c92d67a983e691c9478c8a3fb4117c1f48626bcdea8"
dependencies = [
"bstr",
"gix-attributes",
"gix-command",
"gix-filter",
"gix-fs",
"gix-hash",
"gix-index",
"gix-object",
"gix-path",
"gix-pathspec",
"gix-tempfile",
"gix-trace",
"gix-traverse",
"gix-worktree",
"imara-diff",
"thiserror 2.0.12",
]
[[package]]
name = "gix-dir"
version = "0.14.1"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01e6e2dc5b8917142d0ffe272209d1671e45b771e433f90186bc71c016792e87"
checksum = "dad34e4f373f94902df1ba1d2a1df3a1b29eacd15e316ac5972d842e31422dd7"
dependencies = [
"bstr",
"gix-discover",
@ -1483,9 +1499,9 @@ dependencies = [
[[package]]
name = "gix-discover"
version = "0.40.1"
version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dccfe3e25b4ea46083916c56db3ba9d1e6ef6dce54da485f0463f9fc0fe1837c"
checksum = "ffb180c91ca1a2cf53e828bb63d8d8f8fa7526f49b83b33d7f46cbeb5d79d30a"
dependencies = [
"bstr",
"dunce",
@ -1499,9 +1515,9 @@ dependencies = [
[[package]]
name = "gix-features"
version = "0.42.1"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f4399af6ec4fd9db84dd4cf9656c5c785ab492ab40a7c27ea92b4241923fed"
checksum = "9a92748623c201568785ee69a561f4eec06f745b4fac67dab1d44ca9891a57ee"
dependencies = [
"bytes",
"crc32fast",
@ -1520,9 +1536,9 @@ dependencies = [
[[package]]
name = "gix-filter"
version = "0.19.2"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecf004912949bbcf308d71aac4458321748ecb59f4d046830d25214208c471f1"
checksum = "aa6571a3927e7ab10f64279a088e0dae08e8da05547771796d7389bbe28ad9ff"
dependencies = [
"bstr",
"encoding_rs",
@ -1541,9 +1557,9 @@ dependencies = [
[[package]]
name = "gix-fs"
version = "0.15.0"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67a0637149b4ef24d3ea55f81f77231401c8463fae6da27331c987957eb597c7"
checksum = "d793f71e955d18f228d20ec433dcce6d0e8577efcdfd11d72d09d7cc2758dfd1"
dependencies = [
"bstr",
"fastrand",
@ -1555,9 +1571,9 @@ dependencies = [
[[package]]
name = "gix-glob"
version = "0.20.1"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90181472925b587f6079698f79065ff64786e6d6c14089517a1972bca99fb6e9"
checksum = "b947db8366823e7a750c254f6bb29e27e17f27e457bf336ba79b32423db62cd5"
dependencies = [
"bitflags",
"bstr",
@ -1567,9 +1583,9 @@ dependencies = [
[[package]]
name = "gix-hash"
version = "0.18.0"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d4900562c662852a6b42e2ef03442eccebf24f047d8eab4f23bc12ef0d785d8"
checksum = "251fad79796a731a2a7664d9ea95ee29a9e99474de2769e152238d4fdb69d50e"
dependencies = [
"faster-hex",
"gix-features",
@ -1579,20 +1595,20 @@ dependencies = [
[[package]]
name = "gix-hashtable"
version = "0.8.1"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b5cb3c308b4144f2612ff64e32130e641279fcf1a84d8d40dad843b4f64904"
checksum = "c35300b54896153e55d53f4180460931ccd69b7e8d2f6b9d6401122cdedc4f07"
dependencies = [
"gix-hash",
"hashbrown 0.14.5",
"hashbrown 0.15.4",
"parking_lot",
]
[[package]]
name = "gix-ignore"
version = "0.15.0"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae358c3c96660b10abc7da63c06788dfded603e717edbd19e38c6477911b71c8"
checksum = "564d6fddf46e2c981f571b23d6ad40cb08bddcaf6fc7458b1d49727ad23c2870"
dependencies = [
"bstr",
"gix-glob",
@ -1603,9 +1619,9 @@ dependencies = [
[[package]]
name = "gix-index"
version = "0.40.1"
version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b38e919efd59cb8275d23ad2394b2ab9d002007b27620e145d866d546403b665"
checksum = "2af39fde3ce4ce11371d9ce826f2936ec347318f2d1972fe98c2e7134e267e25"
dependencies = [
"bitflags",
"bstr",
@ -1620,7 +1636,7 @@ dependencies = [
"gix-traverse",
"gix-utils",
"gix-validate",
"hashbrown 0.14.5",
"hashbrown 0.15.4",
"itoa 1.0.15",
"libc",
"memmap2",
@ -1631,9 +1647,9 @@ dependencies = [
[[package]]
name = "gix-lock"
version = "17.1.0"
version = "18.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "570f8b034659f256366dc90f1a24924902f20acccd6a15be96d44d1269e7a796"
checksum = "b9fa71da90365668a621e184eb5b979904471af1b3b09b943a84bc50e8ad42ed"
dependencies = [
"gix-tempfile",
"gix-utils",
@ -1642,9 +1658,9 @@ dependencies = [
[[package]]
name = "gix-negotiate"
version = "0.20.1"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e1ea901acc4d5b44553132a29e8697210cb0e739b2d9752d713072e9391e3c9"
checksum = "1d58d4c9118885233be971e0d7a589f5cfb1a8bd6cb6e2ecfb0fc6b1b293c83b"
dependencies = [
"bitflags",
"gix-commitgraph",
@ -1658,9 +1674,9 @@ dependencies = [
[[package]]
name = "gix-object"
version = "0.49.1"
version = "0.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d957ca3640c555d48bb27f8278c67169fa1380ed94f6452c5590742524c40fbb"
checksum = "49664e3e212bc34f7060f5738ce7022247e4afd959b68a4f666b1fd29c00b23c"
dependencies = [
"bstr",
"gix-actor",
@ -1679,9 +1695,9 @@ dependencies = [
[[package]]
name = "gix-odb"
version = "0.69.1"
version = "0.70.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "868f703905fdbcfc1bd750942f82419903ecb7039f5288adb5206d6de405e0c9"
checksum = "9c9d7af10fda9df0bb4f7f9bd507963560b3c66cb15a5b825caf752e0eb109ac"
dependencies = [
"arc-swap",
"gix-date",
@ -1700,9 +1716,9 @@ dependencies = [
[[package]]
name = "gix-pack"
version = "0.59.1"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d49c55d69c8449f2a0a5a77eb9cbacfebb6b0e2f1215f0fc23a4cb60528a450"
checksum = "d8571df89bfca5abb49c3e3372393f7af7e6f8b8dbe2b96303593cef5b263019"
dependencies = [
"clru",
"gix-chunk",
@ -1720,9 +1736,9 @@ dependencies = [
[[package]]
name = "gix-packetline"
version = "0.19.0"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ddc034bc67c848e4ef7596ab5528cd8fd439d310858dbe1ce8b324f25deb91c"
checksum = "2592fbd36249a2fea11056f7055cc376301ef38d903d157de41998335bbf1f93"
dependencies = [
"bstr",
"faster-hex",
@ -1732,9 +1748,9 @@ dependencies = [
[[package]]
name = "gix-packetline-blocking"
version = "0.19.0"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c44880f028ba46d6cf37a66d27a300310c6b51b8ed0e44918f93df061168e2f3"
checksum = "fc4e706f328cd494cc8f932172e123a72b9a4711b0db5e411681432a89bd4c94"
dependencies = [
"bstr",
"faster-hex",
@ -1744,9 +1760,9 @@ dependencies = [
[[package]]
name = "gix-path"
version = "0.10.18"
version = "0.10.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567f65fec4ef10dfab97ae71f26a27fd4d7fe7b8e3f90c8a58551c41ff3fb65b"
checksum = "c6279d323d925ad4790602105ae27df4b915e7a7d81e4cdba2603121c03ad111"
dependencies = [
"bstr",
"gix-trace",
@ -1758,9 +1774,9 @@ dependencies = [
[[package]]
name = "gix-pathspec"
version = "0.11.0"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce061c50e5f8f7c830cacb3da3e999ae935e283ce8522249f0ce2256d110979d"
checksum = "daedead611c9bd1f3640dc90a9012b45f790201788af4d659f28d94071da7fba"
dependencies = [
"bitflags",
"bstr",
@ -1773,9 +1789,9 @@ dependencies = [
[[package]]
name = "gix-prompt"
version = "0.11.0"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d024a3fe3993bbc17733396d2cefb169c7a9d14b5b71dafb7f96e3962b7c3128"
checksum = "6ffa1a7a34c81710aaa666a428c142b6c5d640492fcd41267db0740d923c7906"
dependencies = [
"gix-command",
"gix-config-value",
@ -1786,9 +1802,9 @@ dependencies = [
[[package]]
name = "gix-protocol"
version = "0.50.1"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5c17d78bb0414f8d60b5f952196dc2e47ec320dca885de9128ecdb4a0e38401"
checksum = "12b4b807c47ffcf7c1e5b8119585368a56449f3493da93b931e1d4239364e922"
dependencies = [
"bstr",
"gix-credentials",
@ -1823,9 +1839,9 @@ dependencies = [
[[package]]
name = "gix-ref"
version = "0.52.1"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1b7985657029684d759f656b09abc3e2c73085596d5cdb494428823970a7762"
checksum = "4b7a23209d4e4cbdc2086d294f5f3f8707ac6286768847024d952d8cd3278c5b"
dependencies = [
"gix-actor",
"gix-features",
@ -1844,9 +1860,9 @@ dependencies = [
[[package]]
name = "gix-refspec"
version = "0.30.1"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "445ed14e3db78e8e79980085e3723df94e1c8163b3ae5bc8ed6a8fe6cf983b42"
checksum = "7d29cae1ae31108826e7156a5e60bffacab405f4413f5bc0375e19772cce0055"
dependencies = [
"bstr",
"gix-hash",
@ -1858,9 +1874,9 @@ dependencies = [
[[package]]
name = "gix-revision"
version = "0.34.1"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78d0b8e5cbd1c329e25383e088cb8f17439414021a643b30afa5146b71e3c65d"
checksum = "f651f2b1742f760bb8161d6743229206e962b73d9c33c41f4e4aefa6586cbd3d"
dependencies = [
"bitflags",
"bstr",
@ -1876,9 +1892,9 @@ dependencies = [
[[package]]
name = "gix-revwalk"
version = "0.20.1"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bc756b73225bf005ddeb871d1ca7b3c33e2417d0d53e56effa5a36765b52b28"
checksum = "06e74f91709729e099af6721bd0fa7d62f243f2005085152301ca5cdd86ec02c"
dependencies = [
"gix-commitgraph",
"gix-date",
@ -1891,9 +1907,9 @@ dependencies = [
[[package]]
name = "gix-sec"
version = "0.11.0"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0dabbc78c759ecc006b970339394951b2c8e1e38a37b072c105b80b84c308fd"
checksum = "09f7053ed7c66633b56c57bc6ed3377be3166eaf3dc2df9f1c5ec446df6fdf2c"
dependencies = [
"bitflags",
"gix-path",
@ -1903,9 +1919,9 @@ dependencies = [
[[package]]
name = "gix-shallow"
version = "0.4.0"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b9a6f6e34d6ede08f522d89e5c7990b4f60524b8ae6ebf8e850963828119ad4"
checksum = "d936745103243ae4c510f19e0760ce73fb0f08096588fdbe0f0d7fb7ce8944b7"
dependencies = [
"bstr",
"gix-hash",
@ -1914,10 +1930,33 @@ dependencies = [
]
[[package]]
name = "gix-submodule"
version = "0.19.1"
name = "gix-status"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f51472f05a450cc61bc91ed2f62fb06e31e2bbb31c420bc4be8793f26c8b0c1"
checksum = "2a4afff9b34eeececa8bdc32b42fb318434b6b1391d9f8d45fe455af08dc2d35"
dependencies = [
"bstr",
"filetime",
"gix-diff",
"gix-dir",
"gix-features",
"gix-filter",
"gix-fs",
"gix-hash",
"gix-index",
"gix-object",
"gix-path",
"gix-pathspec",
"gix-worktree",
"portable-atomic",
"thiserror 2.0.12",
]
[[package]]
name = "gix-submodule"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "657cc5dd43cbc7a14d9c5aaf02cfbe9c2a15d077cded3f304adb30ef78852d3e"
dependencies = [
"bstr",
"gix-config",
@ -1930,10 +1969,11 @@ dependencies = [
[[package]]
name = "gix-tempfile"
version = "17.1.0"
version = "18.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c750e8c008453a2dba67a2b0d928b7716e05da31173a3f5e351d5457ad4470aa"
checksum = "666c0041bcdedf5fa05e9bef663c897debab24b7dc1741605742412d1d47da57"
dependencies = [
"dashmap",
"gix-fs",
"libc",
"once_cell",
@ -1943,15 +1983,15 @@ dependencies = [
[[package]]
name = "gix-trace"
version = "0.1.12"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c396a2036920c69695f760a65e7f2677267ccf483f25046977d87e4cb2665f7"
checksum = "e2ccaf54b0b1743a695b482ca0ab9d7603744d8d10b2e5d1a332fef337bee658"
[[package]]
name = "gix-transport"
version = "0.47.0"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edfe22ba26d4b65c17879f12b9882eafe65d3c8611c933b272fce2c10f546f59"
checksum = "12f7cc0179fc89d53c54e1f9ce51229494864ab4bf136132d69db1b011741ca3"
dependencies = [
"base64",
"bstr",
@ -1968,9 +2008,9 @@ dependencies = [
[[package]]
name = "gix-traverse"
version = "0.46.2"
version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8648172f85aca3d6e919c06504b7ac26baef54e04c55eb0100fa588c102cc33"
checksum = "c7cdc82509d792ba0ad815f86f6b469c7afe10f94362e96c4494525a6601bdd5"
dependencies = [
"bitflags",
"gix-commitgraph",
@ -1985,9 +2025,9 @@ dependencies = [
[[package]]
name = "gix-url"
version = "0.31.0"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42a1ad0b04a5718b5cb233e6888e52a9b627846296161d81dcc5eb9203ec84b8"
checksum = "1b76a9d266254ad287ffd44467cd88e7868799b08f4d52e02d942b93e514d16f"
dependencies = [
"bstr",
"gix-features",
@ -2020,9 +2060,9 @@ dependencies = [
[[package]]
name = "gix-worktree"
version = "0.41.0"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54f1916f8d928268300c977d773dd70a8746b646873b77add0a34876a8c847e9"
checksum = "55f625ac9126c19bef06dbc6d2703cdd7987e21e35b497bb265ac37d383877b1"
dependencies = [
"bstr",
"gix-attributes",
@ -2108,10 +2148,6 @@ name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]]
name = "hashbrown"
@ -2119,6 +2155,8 @@ version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash",
]
@ -2342,6 +2380,15 @@ dependencies = [
"version_check",
]
[[package]]
name = "imara-diff"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17d34b7d42178945f775e84bc4c36dde7c1c6cdfea656d3354d009056f2bb3d2"
dependencies = [
"hashbrown 0.15.4",
]
[[package]]
name = "indexmap"
version = "2.10.0"
@ -3141,11 +3188,10 @@ dependencies = [
[[package]]
name = "prodash"
version = "29.0.2"
version = "30.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f04bb108f648884c23b98a0e940ebc2c93c0c3b89f04dbaf7eb8256ce617d1bc"
checksum = "5a6efc566849d3d9d737c5cb06cc50e48950ebe3d3f9d70631490fff3a07b139"
dependencies = [
"log",
"parking_lot",
]

View File

@ -49,7 +49,7 @@ flate2 = { version = "1.1.2", default-features = false, features = ["zlib-rs"] }
git2 = "0.20.2"
git2-curl = "0.21.0"
# When updating this, also see if `gix-transport` further down needs updating or some auth-related tests will fail.
gix = { version = "0.72.1", default-features = false, features = ["blocking-http-transport-curl", "progress-tree", "parallel", "dirwalk"] }
gix = { version = "0.73.0", default-features = false, features = ["blocking-http-transport-curl", "progress-tree", "parallel", "dirwalk", "status"] }
glob = "0.3.2"
# Pinned due to https://github.com/sunng87/handlebars-rust/issues/711
handlebars = { version = "=6.3.1", features = ["dir_source"] }
@ -257,7 +257,7 @@ cargo-test-support.workspace = true
gix = { workspace = true, features = ["revision"] }
# When building Cargo for tests, a safety-measure in `gix` needs to be disabled
# to allow sending credentials over HTTP connections.
gix-transport = { version = "0.47.0", features = ["http-client-insecure-credentials"] }
gix-transport = { version = "0.48.0", features = ["http-client-insecure-credentials"] }
same-file.workspace = true
snapbox.workspace = true

View File

@ -480,7 +480,6 @@ fn prepare_archive(
// Check (git) repository state, getting the current commit hash.
let vcs_info = vcs::check_repo_state(pkg, &src_files, ws, &opts)?;
build_ar_list(ws, pkg, src_files, vcs_info, opts.include_lockfile)
}

View File

@ -1,21 +1,20 @@
//! Helpers to gather the VCS information for `cargo package`.
use std::collections::HashSet;
use std::path::Path;
use std::path::PathBuf;
use anyhow::Context as _;
use cargo_util::paths;
use serde::Serialize;
use tracing::debug;
use crate::CargoResult;
use crate::GlobalContext;
use crate::core::Package;
use crate::core::Workspace;
use crate::core::{Package, Workspace};
use crate::ops::PackageOpts;
use crate::sources::PathEntry;
use super::PackageOpts;
use crate::{CargoResult, GlobalContext};
use anyhow::Context;
use cargo_util::paths;
use gix::bstr::ByteSlice;
use gix::dir::walk::EmissionMode;
use gix::dirwalk::Options;
use gix::index::entry::Mode;
use gix::status::tree_index::TrackRenames;
use gix::worktree::stack::state::ignore::Source;
use serde::Serialize;
use std::path::{Path, PathBuf};
use tracing::debug;
/// Represents the VCS information when packaging.
#[derive(Serialize)]
@ -29,7 +28,7 @@ pub struct VcsInfo {
#[derive(Serialize)]
pub struct GitVcsInfo {
sha1: String,
/// Indicate whether or not the Git worktree is dirty.
/// Indicate whether the Git worktree is dirty.
#[serde(skip_serializing_if = "std::ops::Not::not")]
dirty: bool,
}
@ -39,7 +38,7 @@ pub struct GitVcsInfo {
/// If *git*, and the source is *dirty* (e.g., has uncommitted changes),
/// and `--allow-dirty` has not been passed,
/// then `bail!` with an informative message.
/// Otherwise return the sha1 hash of the current *HEAD* commit,
/// Otherwise, return the sha1 hash of the current *HEAD* commit,
/// or `None` if no repo is found.
#[tracing::instrument(skip_all)]
pub fn check_repo_state(
@ -49,7 +48,7 @@ pub fn check_repo_state(
opts: &PackageOpts<'_>,
) -> CargoResult<Option<VcsInfo>> {
let gctx = ws.gctx();
let Ok(repo) = git2::Repository::discover(p.root()) else {
let Ok(mut repo) = gix::discover(p.root()) else {
gctx.shell().verbose(|shell| {
shell.warn(format_args!(
"no (git) VCS found for `{}`",
@ -71,21 +70,33 @@ pub fn check_repo_state(
debug!("found a git repo at `{}`", workdir.display());
let path = p.manifest_path();
let manifest_exists = path.exists();
let path = paths::strip_prefix_canonical(path, workdir).unwrap_or_else(|_| path.to_path_buf());
let Ok(status) = repo.status_file(&path) else {
let rela_path =
gix::path::to_unix_separators_on_windows(gix::path::os_str_into_bstr(path.as_os_str())?);
if !manifest_exists {
gctx.shell().verbose(|shell| {
shell.warn(format_args!(
"no (git) Cargo.toml found at `{}` in workdir `{}`",
"Cargo.toml not found at `{}` in workdir `{}`",
path.display(),
workdir.display()
))
})?;
// No checked-in `Cargo.toml` found. This package may be irrelevant.
// No `Cargo.toml` found. This package may be irrelevant.
// Have to assume it is clean.
return Ok(None);
};
if !(status & git2::Status::IGNORED).is_empty() {
let manifest_is_ignored = {
let index = repo.index_or_empty()?;
let mut excludes =
repo.excludes(&index, None, Source::WorktreeThenIdMappingIfNotSkipped)?;
excludes
.at_entry(rela_path.as_bstr(), Some(Mode::FILE))?
.is_excluded()
};
if manifest_is_ignored {
gctx.shell().verbose(|shell| {
shell.warn(format_args!(
"found (git) Cargo.toml ignored at `{}` in workdir `{}`",
@ -105,8 +116,8 @@ pub fn check_repo_state(
path.display(),
workdir.display(),
);
let Some(git) = git(ws, p, src_files, &repo, &opts)? else {
// If the git repo lacks essensial field like `sha1`, and since this field exists from the beginning,
let Some(git) = git(ws, p, src_files, &mut repo, &opts)? else {
// If the git repo lacks essential field like `sha1`, and since this field exists from the beginning,
// then don't generate the corresponding file in order to maintain consistency with past behavior.
return Ok(None);
};
@ -117,7 +128,7 @@ pub fn check_repo_state(
.unwrap_or("")
.replace("\\", "/");
return Ok(Some(VcsInfo { git, path_in_vcs }));
Ok(Some(VcsInfo { git, path_in_vcs }))
}
/// Warns if any symlinks were checked out as plain text files.
@ -136,11 +147,11 @@ pub fn check_repo_state(
fn warn_symlink_checked_out_as_plain_text_file(
gctx: &GlobalContext,
src_files: &[PathEntry],
repo: &git2::Repository,
repo: &gix::Repository,
) -> CargoResult<()> {
if repo
.config()
.and_then(|c| c.get_bool("core.symlinks"))
.config_snapshot()
.boolean(&gix::config::tree::Core::SYMLINKS)
.unwrap_or(true)
{
return Ok(());
@ -149,8 +160,8 @@ fn warn_symlink_checked_out_as_plain_text_file(
if src_files.iter().any(|f| f.maybe_plain_text_symlink()) {
let mut shell = gctx.shell();
shell.warn(format_args!(
"found symbolic links that may be checked out as regular files for git repo at `{}`\n\
This might cause the `.crate` file to include incorrect or incomplete files",
"found symbolic links that may be checked out as regular files for git repo at `{}/`\n\
This might cause the `.crate` file to include incorrect or incomplete files",
repo.workdir().unwrap().display(),
))?;
let extra_note = if cfg!(windows) {
@ -171,17 +182,28 @@ fn git(
ws: &Workspace<'_>,
pkg: &Package,
src_files: &[PathEntry],
repo: &git2::Repository,
repo: &mut gix::Repository,
opts: &PackageOpts<'_>,
) -> CargoResult<Option<GitVcsInfo>> {
{
let mut config = repo.config_snapshot_mut();
// This currently is only a very minor speedup for the biggest repositories,
// but might trigger creating many threads.
config.set_value(&gix::config::tree::Index::THREADS, "false")?;
}
// This is a collection of any dirty or untracked files. This covers:
// - new/modified/deleted/renamed/type change (index or worktree)
// - untracked files (which are "new" worktree files)
// - ignored (in case the user has an `include` directive that
// conflicts with .gitignore).
let mut dirty_files = Vec::new();
let pathspec = relative_pathspec(repo, pkg.root());
collect_statuses(repo, &[pathspec.as_str()], &mut dirty_files)?;
let workdir = repo.workdir().unwrap();
collect_statuses(
repo,
workdir,
relative_package_root(repo, pkg.root()).as_deref(),
&mut dirty_files,
)?;
// Include each submodule so that the error message can provide
// specifically *which* files in a submodule are modified.
@ -193,7 +215,22 @@ fn git(
let cwd = ws.gctx().cwd();
let mut dirty_src_files: Vec<_> = src_files
.iter()
.filter(|src_file| dirty_files.iter().any(|path| src_file.starts_with(path)))
.filter(|src_file| {
if let Some(canon_src_file) = src_file.is_symlink_or_under_symlink().then(|| {
gix::path::realpath_opts(
&src_file,
ws.gctx().cwd(),
gix::path::realpath::MAX_SYMLINKS,
)
.unwrap_or_else(|_| src_file.to_path_buf())
}) {
dirty_files
.iter()
.any(|path| canon_src_file.starts_with(path))
} else {
dirty_files.iter().any(|path| src_file.starts_with(path))
}
})
.map(|p| p.as_ref())
.chain(dirty_files_outside_pkg_root(ws, pkg, repo, src_files)?.iter())
.map(|path| {
@ -206,14 +243,9 @@ fn git(
.collect();
let dirty = !dirty_src_files.is_empty();
if !dirty || opts.allow_dirty {
// Must check whetherthe repo has no commit firstly, otherwise `revparse_single` would fail on bare commit repo.
// Due to lacking the `sha1` field, it's better not record the `GitVcsInfo` for consistency.
if repo.is_empty()? {
return Ok(None);
}
let rev_obj = repo.revparse_single("HEAD")?;
Ok(Some(GitVcsInfo {
sha1: rev_obj.id().to_string(),
let maybe_head_id = repo.head()?.try_peel_to_id_in_place()?;
Ok(maybe_head_id.map(|id| GitVcsInfo {
sha1: id.to_string(),
dirty,
}))
} else {
@ -228,12 +260,97 @@ fn git(
}
}
/// Helper to collect dirty statuses for a single repo.
/// `relative_package_root` is `Some` if the root is a sub-directory of the workdir.
/// Writes dirty files outside `relative_package_root` into `dirty_files_outside_package_root`,
/// and all *everything else* into `dirty_files`.
#[must_use]
fn collect_statuses(
repo: &gix::Repository,
workdir: &Path,
relative_package_root: Option<&Path>,
dirty_files: &mut Vec<PathBuf>,
) -> CargoResult<()> {
let statuses = repo
.status(gix::progress::Discard)?
.dirwalk_options(configure_dirwalk)
.tree_index_track_renames(TrackRenames::Disabled)
.index_worktree_submodules(None)
.into_iter(
relative_package_root.map(|rela_pkg_root| {
gix::path::into_bstr(rela_pkg_root).into_owned()
}), /* pathspec patterns */
)
.with_context(|| {
format!(
"failed to begin git status for repo {}",
repo.path().display()
)
})?;
for status in statuses {
let status = status.with_context(|| {
format!(
"failed to retrieve git status from repo {}",
repo.path().display()
)
})?;
let rel_path = gix::path::from_bstr(status.location());
let path = workdir.join(&rel_path);
// It is OK to include Cargo.lock even if it is ignored.
if path.ends_with("Cargo.lock")
&& matches!(
&status,
gix::status::Item::IndexWorktree(
gix::status::index_worktree::Item::DirectoryContents { entry, .. }
) if matches!(entry.status, gix::dir::entry::Status::Ignored(_))
)
{
continue;
}
dirty_files.push(path);
}
Ok(())
}
/// Helper to collect dirty statuses while recursing into submodules.
fn status_submodules(repo: &gix::Repository, dirty_files: &mut Vec<PathBuf>) -> CargoResult<()> {
let Some(submodules) = repo.submodules()? else {
return Ok(());
};
for submodule in submodules {
// Ignore submodules that don't open, they are probably not initialized.
// If its files are required, then the verification step should fail.
if let Some(sub_repo) = submodule.open()? {
let Some(workdir) = sub_repo.workdir() else {
continue;
};
status_submodules(&sub_repo, dirty_files)?;
collect_statuses(&sub_repo, workdir, None, dirty_files)?;
}
}
Ok(())
}
/// Make `pkg_root` relative to the `repo` workdir.
fn relative_package_root(repo: &gix::Repository, pkg_root: &Path) -> Option<PathBuf> {
let workdir = repo.workdir().unwrap();
let rela_root = pkg_root.strip_prefix(workdir).unwrap_or(Path::new(""));
if rela_root.as_os_str().is_empty() {
None
} else {
rela_root.to_owned().into()
}
}
/// Checks whether "included" source files outside package root have been modified.
///
/// This currently looks at
///
/// * `package.readme` and `package.license-file` pointing to paths outside package root
/// * symlinks targets reside outside package root
/// * symlinks targets residing outside package root
/// * Any change in the root workspace manifest, regardless of what has changed.
///
/// This is required because those paths may link to a file outside the
@ -242,14 +359,12 @@ fn git(
fn dirty_files_outside_pkg_root(
ws: &Workspace<'_>,
pkg: &Package,
repo: &git2::Repository,
repo: &gix::Repository,
src_files: &[PathEntry],
) -> CargoResult<HashSet<PathBuf>> {
) -> CargoResult<Vec<PathBuf>> {
let pkg_root = pkg.root();
let workdir = repo.workdir().unwrap();
let mut dirty_files = HashSet::new();
let meta = pkg.manifest().metadata();
let metadata_paths: Vec<_> = [&meta.license_file, &meta.readme]
.into_iter()
@ -257,7 +372,7 @@ fn dirty_files_outside_pkg_root(
.map(|path| paths::normalize_path(&pkg_root.join(path)))
.collect();
for rel_path in src_files
let linked_files_outside_package_root: Vec<_> = src_files
.iter()
.filter(|p| p.is_symlink_or_under_symlink())
.map(|p| p.as_ref().as_path())
@ -267,82 +382,57 @@ fn dirty_files_outside_pkg_root(
.filter(|p| paths::strip_prefix_canonical(p, pkg_root).is_err())
// Handle files outside package root but under git workdir,
.filter_map(|p| paths::strip_prefix_canonical(p, workdir).ok())
{
match repo.status_file(&rel_path) {
Ok(git2::Status::CURRENT) => {}
Ok(_) => {
dirty_files.insert(workdir.join(rel_path));
}
Err(e) => {
// Dirtiness check for symlinks is mostly informational.
// And changes in submodule would fail git-status as well (see #15384).
// To avoid adding complicated logic to handle that,
// for now we ignore the status check failure.
debug!(
"failed to get status from file `{}` in git repo at `{}`: {e}",
rel_path.display(),
workdir.display()
);
}
}
.collect();
if linked_files_outside_package_root.is_empty() {
return Ok(Vec::new());
}
let statuses = repo
.status(gix::progress::Discard)?
.dirwalk_options(configure_dirwalk)
// Limit the amount of threads for used for the worktree status, as the pathspec will
// prevent most paths from being visited anyway there is not much work.
.index_worktree_options_mut(|opts| opts.thread_limit = Some(1))
.tree_index_track_renames(TrackRenames::Disabled)
.index_worktree_submodules(None)
.into_iter(
linked_files_outside_package_root
.into_iter()
.map(|p| gix::path::into_bstr(p).into_owned()),
)
.with_context(|| {
format!(
"failed to begin git status for outfor repo {}",
repo.path().display()
)
})?;
let mut dirty_files = Vec::new();
for status in statuses {
let status = status.with_context(|| {
format!(
"failed to retrieve git status from repo {}",
repo.path().display()
)
})?;
let rel_path = gix::path::from_bstr(status.location());
let path = workdir.join(&rel_path);
dirty_files.push(path);
}
Ok(dirty_files)
}
/// Helper to collect dirty statuses for a single repo.
fn collect_statuses(
repo: &git2::Repository,
pathspecs: &[&str],
dirty_files: &mut Vec<PathBuf>,
) -> CargoResult<()> {
let mut status_opts = git2::StatusOptions::new();
// Exclude submodules, as they are being handled manually by recursing
// into each one so that details about specific files can be
// retrieved.
pathspecs
.iter()
.fold(&mut status_opts, git2::StatusOptions::pathspec)
.exclude_submodules(true)
.include_ignored(true)
.include_untracked(true);
let repo_statuses = repo.statuses(Some(&mut status_opts)).with_context(|| {
format!(
"failed to retrieve git status from repo {}",
repo.path().display()
)
})?;
let workdir = repo.workdir().unwrap();
let this_dirty = repo_statuses.iter().filter_map(|entry| {
let path = entry.path().expect("valid utf-8 path");
if path.ends_with("Cargo.lock") && entry.status() == git2::Status::IGNORED {
// It is OK to include Cargo.lock even if it is ignored.
return None;
}
// Use an absolute path, so that comparing paths is easier
// (particularly with submodules).
Some(workdir.join(path))
});
dirty_files.extend(this_dirty);
Ok(())
}
/// Helper to collect dirty statuses while recursing into submodules.
fn status_submodules(repo: &git2::Repository, dirty_files: &mut Vec<PathBuf>) -> CargoResult<()> {
for submodule in repo.submodules()? {
// Ignore submodules that don't open, they are probably not initialized.
// If its files are required, then the verification step should fail.
if let Ok(sub_repo) = submodule.open() {
status_submodules(&sub_repo, dirty_files)?;
collect_statuses(&sub_repo, &[], dirty_files)?;
}
}
Ok(())
}
/// Use pathspec so git only matches a certain path prefix
fn relative_pathspec(repo: &git2::Repository, pkg_root: &Path) -> String {
let workdir = repo.workdir().unwrap();
let relpath = pkg_root.strip_prefix(workdir).unwrap_or(Path::new(""));
// to unix separators
relpath.to_str().unwrap().replace('\\', "/")
fn configure_dirwalk(opts: Options) -> Options {
opts.emit_untracked(gix::dir::walk::EmissionMode::Matching)
// Also pick up ignored files or whole directories
// to specifically catch overzealously ignored source files.
// Later we will match these dirs by prefix, which is why collapsing
// them is desirable here.
.emit_ignored(Some(EmissionMode::CollapseDirectory))
.emit_tracked(false)
.recurse_repositories(false)
.symlinks_to_directories_are_ignored_like_directories(true)
.emit_empty_directories(false)
}

View File

@ -3162,9 +3162,10 @@ fn dirty_submodule() {
.with_stderr_data(str![[r#"
[WARNING] manifest has no description, license, license-file, documentation, homepage or repository.
See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info.
[ERROR] 1 files in the working directory contain changes that were not yet committed into git:
[ERROR] 2 files in the working directory contain changes that were not yet committed into git:
.gitmodules
src/lib.rs
to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag
@ -3208,9 +3209,10 @@ to proceed despite this and include the uncommitted changes, pass the `--allow-d
.with_stderr_data(str![[r#"
[WARNING] manifest has no description, license, license-file, documentation, homepage or repository.
See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info.
[ERROR] 1 files in the working directory contain changes that were not yet committed into git:
[ERROR] 2 files in the working directory contain changes that were not yet committed into git:
src/.gitmodules
src/bar/mod.rs
to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag

View File

@ -1347,7 +1347,7 @@ fn dirty_file_outside_pkg_root_considered_dirty() {
git::commit(&repo);
// Changing files outside pkg root under situations below should be treated
// as dirty. `cargo package` is expected to fail on VCS stastus check.
// as dirty. `cargo package` is expected to fail on VCS status check.
//
// * Changes in files outside package root that source files symlink to
p.change_file("README.md", "after");
@ -1355,7 +1355,7 @@ fn dirty_file_outside_pkg_root_considered_dirty() {
p.change_file("original-dir/file", "after");
// * Changes in files outside pkg root that `license-file`/`readme` point to
p.change_file("LICENSE", "after");
// * When workspace root manifest has changned,
// * When workspace root manifest has changed,
// no matter whether workspace inheritance is involved.
p.change_file(
"Cargo.toml",
@ -1367,7 +1367,7 @@ fn dirty_file_outside_pkg_root_considered_dirty() {
edition = "2021"
"#,
);
// Changes in files outside git workdir won't affect vcs status check
// Changes in files outside git workdir won't affect VCS status check
p.change_file(
&main_outside_pkg_root,
r#"fn main() { eprintln!("after"); }"#,
@ -1470,13 +1470,16 @@ fn dirty_file_outside_pkg_root_inside_submodule() {
p.symlink("submodule/file.txt", "isengard/src/file.txt");
git::add(&repo);
git::commit(&repo);
// This dirtyness should be detected in the future.
p.change_file("submodule/file.txt", "changed");
p.cargo("package --workspace --no-verify")
.with_status(101)
.with_stderr_data(str![[r#"
[PACKAGING] isengard v0.0.0 ([ROOT]/foo/isengard)
[PACKAGED] 6 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
[ERROR] 1 files in the working directory contain changes that were not yet committed into git:
isengard/src/file.txt
to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag
"#]])
.run();