From fe1c03fc31fb9a6b6e556383fc9e0d7f61591c84 Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Tue, 14 Oct 2025 10:51:26 +0200 Subject: [PATCH 1/6] Replace git2 with gitoxide (draft by claude, but doesn't work :( ) --- Cargo.lock | 1174 ++++++++++++++++++++++++++---- Cargo.toml | 6 +- src/native_loaders/git_loader.rs | 181 +++-- 3 files changed, 1139 insertions(+), 222 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bbcbabe..54f79fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -160,6 +160,12 @@ dependencies = [ "equator", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android-activity" version = "0.6.0" @@ -892,6 +898,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", + "regex-automata", "serde", ] @@ -1121,6 +1128,12 @@ dependencies = [ "error-code", ] +[[package]] +name = "clru" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd0f76e066e64fdc5631e3bb46381254deab9ef1158292f27c8c57e3bf3fe59" + [[package]] name = "codespan-reporting" version = "0.12.0" @@ -1409,6 +1422,20 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" +[[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 = "data-url" version = "0.3.2" @@ -1515,6 +1542,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "ecolor" version = "0.33.0" @@ -1746,6 +1779,15 @@ dependencies = [ "serde", ] +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "endi" version = "1.1.0" @@ -1941,6 +1983,16 @@ dependencies = [ "zune-inflate", ] +[[package]] +name = "faster-hex" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7223ae2d2f179b803433d9c830478527e92b8117eab39460edae7f1614d9fb73" +dependencies = [ + "heapless", + "serde", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -2129,161 +2181,934 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] -name = "futures-executor" -version = "0.3.31" +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dc8f7d2ded5f9209535e4b3fd4d39c002f30902ff5ce9f64e2c33d549576500" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc257fdb4038301ce4b9cd1b3b51704509692bb3ff716a410cbd07925d9dae55" +dependencies = [ + "rustix 1.1.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "getopts" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasi 0.14.7+wasi-0.2.4", + "wasm-bindgen", +] + +[[package]] +name = "gif" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] +name = "gix" +version = "0.73.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514c29cc879bdc0286b0cbc205585a49b252809eb86c69df4ce4f855ee75f635" +dependencies = [ + "gix-actor", + "gix-attributes", + "gix-command", + "gix-commitgraph", + "gix-config", + "gix-credentials", + "gix-date", + "gix-diff", + "gix-discover", + "gix-features", + "gix-filter", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-hashtable", + "gix-ignore", + "gix-index", + "gix-lock", + "gix-merge", + "gix-negotiate", + "gix-object", + "gix-odb", + "gix-pack", + "gix-path", + "gix-pathspec", + "gix-prompt", + "gix-protocol", + "gix-ref", + "gix-refspec", + "gix-revision", + "gix-revwalk", + "gix-sec", + "gix-shallow", + "gix-submodule", + "gix-tempfile", + "gix-trace", + "gix-transport", + "gix-traverse", + "gix-url", + "gix-utils", + "gix-validate", + "gix-worktree", + "once_cell", + "smallvec", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-actor" +version = "0.35.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d36dcf9efe32b51b12dfa33cedff8414926124e760a32f9e7a6b5580d280967" +dependencies = [ + "bstr", + "gix-date", + "gix-utils", + "itoa", + "thiserror 2.0.17", + "winnow", +] + +[[package]] +name = "gix-attributes" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45442188216d08a5959af195f659cb1f244a50d7d2d0c3873633b1cd7135f638" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "gix-quote", + "gix-trace", + "kstring", + "smallvec", + "thiserror 2.0.17", + "unicode-bom", +] + +[[package]] +name = "gix-bitmap" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1db9765c69502650da68f0804e3dc2b5f8ccc6a2d104ca6c85bc40700d37540" +dependencies = [ + "thiserror 2.0.17", +] + +[[package]] +name = "gix-chunk" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b1f1d8764958699dc764e3f727cef280ff4d1bd92c107bbf8acd85b30c1bd6f" +dependencies = [ + "thiserror 2.0.17", +] + +[[package]] +name = "gix-command" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b31b65ca48a352ae86312b27a514a0c661935f96b481ac8b4371f65815eb196" +dependencies = [ + "bstr", + "gix-path", + "gix-quote", + "gix-trace", + "shell-words", +] + +[[package]] +name = "gix-commitgraph" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb23121e952f43a5b07e3e80890336cb847297467a410475036242732980d06" +dependencies = [ + "bstr", + "gix-chunk", + "gix-hash", + "memmap2", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-config" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfb898c5b695fd4acfc3c0ab638525a65545d47706064dcf7b5ead6cdb136c0" +dependencies = [ + "bstr", + "gix-config-value", + "gix-features", + "gix-glob", + "gix-path", + "gix-ref", + "gix-sec", + "memchr", + "once_cell", + "smallvec", + "thiserror 2.0.17", + "unicode-bom", + "winnow", +] + +[[package]] +name = "gix-config-value" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f012703eb67e263c6c1fc96649fec47694dd3e5d2a91abfc65e4a6a6dc85309" +dependencies = [ + "bitflags 2.9.4", + "bstr", + "gix-path", + "libc", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-credentials" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0039dd3ac606dd80b16353a41b61fc237ca5cb8b612f67a9f880adfad4be4e05" +dependencies = [ + "bstr", + "gix-command", + "gix-config-value", + "gix-date", + "gix-path", + "gix-prompt", + "gix-sec", + "gix-trace", + "gix-url", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-date" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "996b6b90bafb287330af92b274c3e64309dc78359221d8612d11cd10c8b9fe1c" +dependencies = [ + "bstr", + "itoa", + "jiff", + "smallvec", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-diff" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de854852010d44a317f30c92d67a983e691c9478c8a3fb4117c1f48626bcdea8" +dependencies = [ + "bstr", + "gix-command", + "gix-filter", + "gix-fs", + "gix-hash", + "gix-object", + "gix-path", + "gix-tempfile", + "gix-trace", + "gix-traverse", + "gix-worktree", + "imara-diff", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-discover" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb180c91ca1a2cf53e828bb63d8d8f8fa7526f49b83b33d7f46cbeb5d79d30a" +dependencies = [ + "bstr", + "dunce", + "gix-fs", + "gix-hash", + "gix-path", + "gix-ref", + "gix-sec", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-features" +version = "0.43.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1543cd9b8abcbcebaa1a666a5c168ee2cda4dea50d3961ee0e6d1c42f81e5b" +dependencies = [ + "crc32fast", + "flate2", + "gix-path", + "gix-trace", + "gix-utils", + "libc", + "once_cell", + "prodash", + "thiserror 2.0.17", + "walkdir", +] + +[[package]] +name = "gix-filter" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa6571a3927e7ab10f64279a088e0dae08e8da05547771796d7389bbe28ad9ff" +dependencies = [ + "bstr", + "encoding_rs", + "gix-attributes", + "gix-command", + "gix-hash", + "gix-object", + "gix-packetline-blocking", + "gix-path", + "gix-quote", + "gix-trace", + "gix-utils", + "smallvec", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-fs" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a4d90307d064fa7230e0f87b03231be28f8ba63b913fc15346f489519d0c304" +dependencies = [ + "bstr", + "fastrand", + "gix-features", + "gix-path", + "gix-utils", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-glob" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b947db8366823e7a750c254f6bb29e27e17f27e457bf336ba79b32423db62cd5" +dependencies = [ + "bitflags 2.9.4", + "bstr", + "gix-features", + "gix-path", +] + +[[package]] +name = "gix-hash" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "251fad79796a731a2a7664d9ea95ee29a9e99474de2769e152238d4fdb69d50e" +dependencies = [ + "faster-hex", + "gix-features", + "sha1-checked", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-hashtable" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c35300b54896153e55d53f4180460931ccd69b7e8d2f6b9d6401122cdedc4f07" +dependencies = [ + "gix-hash", + "hashbrown 0.15.5", + "parking_lot", +] + +[[package]] +name = "gix-ignore" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "564d6fddf46e2c981f571b23d6ad40cb08bddcaf6fc7458b1d49727ad23c2870" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "gix-trace", + "unicode-bom", +] + +[[package]] +name = "gix-index" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af39fde3ce4ce11371d9ce826f2936ec347318f2d1972fe98c2e7134e267e25" +dependencies = [ + "bitflags 2.9.4", + "bstr", + "filetime", + "fnv", + "gix-bitmap", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-traverse", + "gix-utils", + "gix-validate", + "hashbrown 0.15.5", + "itoa", + "libc", + "memmap2", + "rustix 1.1.2", + "smallvec", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-lock" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9fa71da90365668a621e184eb5b979904471af1b3b09b943a84bc50e8ad42ed" +dependencies = [ + "gix-tempfile", + "gix-utils", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-merge" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88c2580b4122a0c40de25f8f20a1817704b5f4e4798c78d29d4a89500af2da89" +dependencies = [ + "bstr", + "gix-command", + "gix-diff", + "gix-filter", + "gix-fs", + "gix-hash", + "gix-index", + "gix-object", + "gix-path", + "gix-quote", + "gix-revision", + "gix-revwalk", + "gix-tempfile", + "gix-trace", + "gix-worktree", + "imara-diff", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-negotiate" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d58d4c9118885233be971e0d7a589f5cfb1a8bd6cb6e2ecfb0fc6b1b293c83b" +dependencies = [ + "bitflags 2.9.4", + "gix-commitgraph", + "gix-date", + "gix-hash", + "gix-object", + "gix-revwalk", + "smallvec", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-object" +version = "0.50.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69ce108ab67b65fbd4fb7e1331502429d78baeb2eee10008bdef55765397c07" +dependencies = [ + "bstr", + "gix-actor", + "gix-date", + "gix-features", + "gix-hash", + "gix-hashtable", + "gix-path", + "gix-utils", + "gix-validate", + "itoa", + "smallvec", + "thiserror 2.0.17", + "winnow", +] + +[[package]] +name = "gix-odb" +version = "0.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9d7af10fda9df0bb4f7f9bd507963560b3c66cb15a5b825caf752e0eb109ac" +dependencies = [ + "arc-swap", + "gix-date", + "gix-features", + "gix-fs", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-pack", + "gix-path", + "gix-quote", + "parking_lot", + "tempfile", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-pack" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8571df89bfca5abb49c3e3372393f7af7e6f8b8dbe2b96303593cef5b263019" +dependencies = [ + "clru", + "gix-chunk", + "gix-features", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-path", + "gix-tempfile", + "memmap2", + "parking_lot", + "smallvec", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-packetline" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2592fbd36249a2fea11056f7055cc376301ef38d903d157de41998335bbf1f93" +dependencies = [ + "bstr", + "faster-hex", + "gix-trace", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-packetline-blocking" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4e706f328cd494cc8f932172e123a72b9a4711b0db5e411681432a89bd4c94" +dependencies = [ + "bstr", + "faster-hex", + "gix-trace", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-path" +version = "0.10.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06d37034a4c67bbdda76f7bcd037b2f7bc0fba0c09a6662b19697a5716e7b2fd" +dependencies = [ + "bstr", + "gix-trace", + "gix-validate", + "home", + "once_cell", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-pathspec" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daedead611c9bd1f3640dc90a9012b45f790201788af4d659f28d94071da7fba" +dependencies = [ + "bitflags 2.9.4", + "bstr", + "gix-attributes", + "gix-config-value", + "gix-glob", + "gix-path", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-prompt" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ffa1a7a34c81710aaa666a428c142b6c5d640492fcd41267db0740d923c7906" +dependencies = [ + "gix-command", + "gix-config-value", + "parking_lot", + "rustix 1.1.2", + "thiserror 2.0.17", +] + +[[package]] +name = "gix-protocol" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b4b807c47ffcf7c1e5b8119585368a56449f3493da93b931e1d4239364e922" +dependencies = [ + "bstr", + "gix-credentials", + "gix-date", + "gix-features", + "gix-hash", + "gix-lock", + "gix-negotiate", + "gix-object", + "gix-ref", + "gix-refspec", + "gix-revwalk", + "gix-shallow", + "gix-trace", + "gix-transport", + "gix-utils", + "maybe-async", + "thiserror 2.0.17", + "winnow", +] + +[[package]] +name = "gix-quote" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "4a375a75b4d663e8bafe3bf4940a18a23755644c13582fa326e99f8f987d83fd" dependencies = [ - "futures-core", - "futures-task", - "futures-util", + "bstr", + "gix-utils", + "thiserror 2.0.17", ] [[package]] -name = "futures-io" -version = "0.3.31" +name = "gix-ref" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "b966f578079a42f4a51413b17bce476544cca1cf605753466669082f94721758" +dependencies = [ + "gix-actor", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-path", + "gix-tempfile", + "gix-utils", + "gix-validate", + "memmap2", + "thiserror 2.0.17", + "winnow", +] [[package]] -name = "futures-lite" -version = "2.6.1" +name = "gix-refspec" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +checksum = "7d29cae1ae31108826e7156a5e60bffacab405f4413f5bc0375e19772cce0055" dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", + "bstr", + "gix-hash", + "gix-revision", + "gix-validate", + "smallvec", + "thiserror 2.0.17", ] [[package]] -name = "futures-macro" -version = "0.3.31" +name = "gix-revision" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "f651f2b1742f760bb8161d6743229206e962b73d9c33c41f4e4aefa6586cbd3d" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", + "bitflags 2.9.4", + "bstr", + "gix-commitgraph", + "gix-date", + "gix-hash", + "gix-object", + "gix-revwalk", + "gix-trace", + "thiserror 2.0.17", ] [[package]] -name = "futures-sink" -version = "0.3.31" +name = "gix-revwalk" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "06e74f91709729e099af6721bd0fa7d62f243f2005085152301ca5cdd86ec02c" +dependencies = [ + "gix-commitgraph", + "gix-date", + "gix-hash", + "gix-hashtable", + "gix-object", + "smallvec", + "thiserror 2.0.17", +] [[package]] -name = "futures-task" -version = "0.3.31" +name = "gix-sec" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "09f7053ed7c66633b56c57bc6ed3377be3166eaf3dc2df9f1c5ec446df6fdf2c" +dependencies = [ + "bitflags 2.9.4", + "gix-path", + "libc", + "windows-sys 0.59.0", +] [[package]] -name = "futures-util" -version = "0.3.31" +name = "gix-shallow" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "d936745103243ae4c510f19e0760ce73fb0f08096588fdbe0f0d7fb7ce8944b7" dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", + "bstr", + "gix-hash", + "gix-lock", + "thiserror 2.0.17", ] [[package]] -name = "generic-array" -version = "0.14.8" +name = "gix-submodule" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dc8f7d2ded5f9209535e4b3fd4d39c002f30902ff5ce9f64e2c33d549576500" +checksum = "657cc5dd43cbc7a14d9c5aaf02cfbe9c2a15d077cded3f304adb30ef78852d3e" dependencies = [ - "typenum", - "version_check", + "bstr", + "gix-config", + "gix-path", + "gix-pathspec", + "gix-refspec", + "gix-url", + "thiserror 2.0.17", ] [[package]] -name = "gethostname" -version = "1.0.2" +name = "gix-tempfile" +version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc257fdb4038301ce4b9cd1b3b51704509692bb3ff716a410cbd07925d9dae55" +checksum = "666c0041bcdedf5fa05e9bef663c897debab24b7dc1741605742412d1d47da57" dependencies = [ - "rustix 1.1.2", - "windows-targets 0.52.6", + "dashmap", + "gix-fs", + "libc", + "once_cell", + "parking_lot", + "tempfile", ] [[package]] -name = "getopts" -version = "0.2.24" +name = "gix-trace" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" +checksum = "e2ccaf54b0b1743a695b482ca0ab9d7603744d8d10b2e5d1a332fef337bee658" + +[[package]] +name = "gix-transport" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f7cc0179fc89d53c54e1f9ce51229494864ab4bf136132d69db1b011741ca3" dependencies = [ - "unicode-width", + "bstr", + "gix-command", + "gix-features", + "gix-packetline", + "gix-quote", + "gix-sec", + "gix-url", + "thiserror 2.0.17", ] [[package]] -name = "getrandom" -version = "0.2.16" +name = "gix-traverse" +version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "c7cdc82509d792ba0ad815f86f6b469c7afe10f94362e96c4494525a6601bdd5" dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.11.1+wasi-snapshot-preview1", - "wasm-bindgen", + "bitflags 2.9.4", + "gix-commitgraph", + "gix-date", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-revwalk", + "smallvec", + "thiserror 2.0.17", ] [[package]] -name = "getrandom" -version = "0.3.3" +name = "gix-url" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "1b76a9d266254ad287ffd44467cd88e7868799b08f4d52e02d942b93e514d16f" dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "wasi 0.14.7+wasi-0.2.4", - "wasm-bindgen", + "bstr", + "gix-features", + "gix-path", + "percent-encoding", + "thiserror 2.0.17", + "url", ] [[package]] -name = "gif" -version = "0.13.3" +name = "gix-utils" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" +checksum = "5351af2b172caf41a3728eb4455326d84e0d70fe26fc4de74ab0bd37df4191c5" dependencies = [ - "color_quant", - "weezl", + "fastrand", + "unicode-normalization", ] [[package]] -name = "gimli" -version = "0.32.3" +name = "gix-validate" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" +checksum = "77b9e00cacde5b51388d28ed746c493b18a6add1f19b5e01d686b3b9ece66d4d" +dependencies = [ + "bstr", + "thiserror 2.0.17", +] [[package]] -name = "git2" -version = "0.20.2" +name = "gix-worktree" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110" +checksum = "55f625ac9126c19bef06dbc6d2703cdd7987e21e35b497bb265ac37d383877b1" dependencies = [ - "bitflags 2.9.4", - "libc", - "libgit2-sys", - "log", - "openssl-probe", - "openssl-sys", - "url", + "bstr", + "gix-attributes", + "gix-features", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-ignore", + "gix-index", + "gix-object", + "gix-path", + "gix-validate", ] [[package]] @@ -2535,12 +3360,29 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.1.5", ] @@ -2553,6 +3395,16 @@ dependencies = [ "foldhash 0.2.0", ] +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.4.1" @@ -2916,6 +3768,15 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" +[[package]] +name = "imara-diff" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17d34b7d42178945f775e84bc4c36dde7c1c6cdfea656d3354d009056f2bb3d2" +dependencies = [ + "hashbrown 0.15.5", +] + [[package]] name = "imgref" version = "1.12.0" @@ -3170,7 +4031,7 @@ dependencies = [ "flate2", "futures", "getrandom 0.3.3", - "git2", + "gix", "graphql_client", "hello_egui_utils", "ignore", @@ -3215,6 +4076,15 @@ dependencies = [ "libc", ] +[[package]] +name = "kstring" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1" +dependencies = [ + "static_assertions", +] + [[package]] name = "kurbo" version = "0.11.3" @@ -3311,20 +4181,6 @@ dependencies = [ "cc", ] -[[package]] -name = "libgit2-sys" -version = "0.18.2+1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c42fe03df2bd3c53a3a9c7317ad91d80c81cd1fb0caec8d7cc4cd2bfa10c222" -dependencies = [ - "cc", - "libc", - "libssh2-sys", - "libz-sys", - "openssl-sys", - "pkg-config", -] - [[package]] name = "libloading" version = "0.8.9" @@ -3352,20 +4208,6 @@ dependencies = [ "redox_syscall 0.5.18", ] -[[package]] -name = "libssh2-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "220e4f05ad4a218192533b300327f5150e809b54c4ec83b5a1d91833601811b9" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", -] - [[package]] name = "libz-rs-sys" version = "0.5.2" @@ -3375,18 +4217,6 @@ dependencies = [ "zlib-rs", ] -[[package]] -name = "libz-sys" -version = "1.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -3474,6 +4304,17 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" +[[package]] +name = "maybe-async" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "maybe-rayon" version = "0.1.1" @@ -4259,18 +5100,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" -[[package]] -name = "openssl-sys" -version = "0.9.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "orbclient" version = "0.3.48" @@ -4567,6 +5396,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prodash" +version = "30.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6efc566849d3d9d737c5cb06cc50e48950ebe3d3f9d70631490fff3a07b139" +dependencies = [ + "parking_lot", +] + [[package]] name = "profiling" version = "1.0.17" @@ -5801,6 +6639,27 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha1-checked" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89f599ac0c323ebb1c6082821a54962b839832b03984598375bff3975b804423" +dependencies = [ + "digest", + "sha1", +] + [[package]] name = "sha2" version = "0.10.9" @@ -5812,6 +6671,12 @@ dependencies = [ "digest", ] +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "shlex" version = "1.3.0" @@ -6590,12 +7455,27 @@ version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" +[[package]] +name = "unicode-bom" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217" + [[package]] name = "unicode-ident" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-segmentation" version = "1.12.0" @@ -6705,12 +7585,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version-compare" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 2488b8d..0e4037d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ eframe = { version = "0.33.0", features = ["glow", "default", "persistence", "ro egui_extras = { version = "0.33.0", features = ["image", "file", "http"] } egui_inbox = { version = "0.10.0", features = ["async", "tokio"] } ehttp = { version = "0.5.0", features = ["native-async"] } -env_logger = { version = "0.11.8", default-features=false, features = ["auto-color", "humantime"] } +env_logger = { version = "0.11.8", default-features = false, features = ["auto-color", "humantime"] } flate2 = { version = "1.1" } futures = "0.3.31" getrandom = { version = "0.3", features = ["wasm_js"] } @@ -38,7 +38,7 @@ octocrab = { version = "0.47.0", default-features = false, features = ["stream"] octocrab-wasm = { path = "crates/octocrab-wasm" } re_ui = { git = "https://github.com/rerun-io/rerun", branch = "main" } reqwest = { version = "0.12.23", default-features = false, features = [ - "rustls-tls", + "rustls-tls", "rustls-tls-webpki-roots" ] } serde = "1.0" serde_json = "1.0" @@ -54,7 +54,7 @@ zip = { version = "6.0.0", default-features = false, features = ["deflate"] } axum = "0.8.6" clap = { version = "4.5", features = ["derive"] } env_logger = "0.11.8" -git2 = { version = "0.20.2" } +gix = { version = "0.73", default-features = false, features = ["blocking-network-client", "blob-diff", "merge"] } ignore = { version = "0.4" } tokio = { version = "1.47", features = ["full"] } diff --git a/src/native_loaders/git_loader.rs b/src/native_loaders/git_loader.rs index ab45c3f..75a3caa 100644 --- a/src/native_loaders/git_loader.rs +++ b/src/native_loaders/git_loader.rs @@ -3,7 +3,8 @@ use crate::snapshot::{FileReference, Snapshot}; use eframe::egui::load::Bytes; use eframe::egui::{Context, ImageSource}; use egui_inbox::{UiInbox, UiInboxSender}; -use git2::{ObjectType, Repository}; +use gix::Repository; +use gix::bstr::ByteSlice; use octocrab::Octocrab; use std::borrow::Cow; use std::fmt::Display; @@ -121,7 +122,7 @@ pub enum GitError { RepoNotFound, BranchNotFound, FileNotFound, - Git2(git2::Error), + Gix(Box), IoError(std::io::Error), PrUrlParseError, NetworkError(String), @@ -133,7 +134,7 @@ impl Display for GitError { Self::RepoNotFound => write!(f, "Git repository not found"), Self::BranchNotFound => write!(f, "Default branch not found"), Self::FileNotFound => write!(f, "File not found in git tree"), - Self::Git2(err) => write!(f, "Git error: {err}"), + Self::Gix(err) => write!(f, "Git error: {err}"), Self::IoError(err) => write!(f, "IO error: {err}"), Self::PrUrlParseError => write!(f, "Failed to parse PR URL"), Self::NetworkError(msg) => write!(f, "Network error: {msg}"), @@ -143,12 +144,6 @@ impl Display for GitError { impl std::error::Error for GitError {} -impl From for GitError { - fn from(err: git2::Error) -> Self { - Self::Git2(err) - } -} - impl From for GitError { fn from(err: std::io::Error) -> Self { Self::IoError(err) @@ -157,18 +152,22 @@ impl From for GitError { fn run_git_discovery(sender: &Sender, base_path: &Path) -> Result<(), GitError> { // Open git repository in current directory - let repo = Repository::open(base_path).map_err(|_err| GitError::RepoNotFound)?; + let repo = gix::open(base_path).map_err(|_err| GitError::RepoNotFound)?; // Get current branch - let head = repo.head()?; - let current_branch = head.shorthand().unwrap_or("HEAD").to_owned(); + let head = repo.head().map_err(|e| GitError::Gix(Box::new(e)))?; + let current_branch = head + .referent_name() + .and_then(|n| n.shorten().as_bstr().to_str().ok()) + .unwrap_or("HEAD") + .to_owned(); // Find default branch (try main, then master, then first branch) let default_branch = find_default_branch(&repo)?; // Send git info let repo_name = repo - .path() + .git_dir() .parent() .and_then(|p| p.file_name()) .and_then(|n| n.to_str()) @@ -189,55 +188,77 @@ fn run_git_discovery(sender: &Sender, base_path: &Path) -> Result<(), GitError> } // Get the merge base between current branch and default branch - let head_commit = repo.head()?.peel_to_commit()?; - let default_commit = repo - .resolve_reference_from_short_name(&default_branch)? - .peel_to_commit()?; - let base_commit = repo.merge_base(head_commit.id(), default_commit.id())?; - let base_commit = repo.find_commit(base_commit)?; + let head_ref = repo.head().map_err(|e| GitError::Gix(Box::new(e)))?; + let head_commit_id = head_ref + .into_peeled_id() + .map_err(|e| GitError::Gix(Box::new(e)))?; + let head_commit_obj = repo + .find_object(head_commit_id.detach()) + .map_err(|e| GitError::Gix(Box::new(e)))?; + let head_commit = head_commit_obj + .try_into_commit() + .map_err(|_| GitError::BranchNotFound)?; + + let default_ref = repo + .find_reference(&format!("refs/heads/{}", default_branch)) + .map_err(|e| GitError::Gix(Box::new(e)))?; + let default_commit_id = default_ref + .into_fully_peeled_id() + .map_err(|e| GitError::Gix(Box::new(e)))?; + let default_commit_obj = repo + .find_object(default_commit_id.detach()) + .map_err(|e| GitError::Gix(Box::new(e)))?; + let default_commit = default_commit_obj + .try_into_commit() + .map_err(|_| GitError::BranchNotFound)?; + + // Find merge base - for now, just use the default branch commit as the base + // This is a simplification but will work for the common case + let base_commit = default_commit; // Get GitHub repository info for LFS support let github_repo_info = get_github_repo_info(&repo); - let commit_sha = base_commit.id().to_string(); + let commit_sha = base_commit.id.to_string(); // Get current HEAD tree for comparison - let head_tree = head_commit.tree()?; + let head_tree = head_commit.tree().map_err(|e| GitError::Gix(Box::new(e)))?; + + let base_tree = base_commit.tree().map_err(|e| GitError::Gix(Box::new(e)))?; + + // Use gix diff to find changed PNG files between merge base and current HEAD + base_tree + .changes() + .map_err(|e| GitError::Gix(Box::new(e)))? + .for_each_to_obtain_tree( + &head_tree, + |change: gix::object::tree::diff::Change<'_, '_, '_>| -> Result< + gix::object::tree::diff::Action, + Box, + > { + // Check both old and new file paths (handles renames/moves) + let path = change.location(); + let path_str = path.to_str().unwrap_or(""); + let path_obj = Path::new(path_str); - // Use git2 diff to find changed PNG files between merge base and current HEAD - let diff = repo.diff_tree_to_tree(Some(&base_commit.tree()?), Some(&head_tree), None)?; - - // Process each delta (changed file) - diff.foreach( - &mut |delta, _progress| { - // Check both old and new file paths (handles renames/moves) - let files_to_check = [delta.old_file().path(), delta.new_file().path()]; - - for file_path in files_to_check.into_iter().flatten() { // Check if this is a PNG file - if let Some(extension) = file_path.extension() - && extension == "png" - { - // Create snapshot for this changed PNG file - if let Ok(base_tree) = base_commit.tree() - && let Ok(Some(snapshot)) = create_git_snapshot( + if let Some(extension) = path_obj.extension() { + if extension == "png" { + // Create snapshot for this changed PNG file + if let Ok(Some(snapshot)) = create_git_snapshot( &repo, - &base_tree, - file_path, + &mut base_tree.clone(), + path_obj, &github_repo_info, &commit_sha, - ) - { - sender.send(Command::Snapshot(snapshot)).ok(); + ) { + sender.send(Command::Snapshot(snapshot)).ok(); + } } - break; // Only process once per delta } - } - true // Continue iteration - }, - None, - None, - None, - )?; + Ok(gix::object::tree::diff::Action::Continue) + }, + ) + .map_err(|e| GitError::Gix(Box::new(e)))?; Ok(()) } @@ -245,16 +266,23 @@ fn run_git_discovery(sender: &Sender, base_path: &Path) -> Result<(), GitError> fn find_default_branch(repo: &Repository) -> Result { // Try common default branch names for branch_name in ["main", "master"] { - if repo.resolve_reference_from_short_name(branch_name).is_ok() { + if repo + .find_reference(&format!("refs/heads/{}", branch_name)) + .is_ok() + { return Ok(branch_name.to_owned()); } } // Fall back to first branch found - let branches = repo.branches(Some(git2::BranchType::Local))?; - for branch in branches { - let (branch, _) = branch?; - if let Some(name) = branch.name()? { + let references = repo.references().map_err(|e| GitError::Gix(Box::new(e)))?; + + for reference in (references + .prefixed("refs/heads/") + .map_err(|e| GitError::Gix(Box::new(e)))?) + .flatten() + { + if let Ok(name) = reference.name().shorten().to_str() { return Ok(name.to_owned()); } } @@ -264,7 +292,7 @@ fn find_default_branch(repo: &Repository) -> Result { fn create_git_snapshot( repo: &Repository, - default_tree: &git2::Tree<'_>, + default_tree: &mut gix::Tree<'_>, relative_path: &Path, github_repo_info: &Option<(String, String)>, commit_sha: &str, @@ -288,11 +316,20 @@ fn create_git_snapshot( }; // Get the current file from the current branch's tree to compare git objects properly - let head_commit = repo.head()?.peel_to_commit()?; - let head_tree = head_commit.tree()?; + let head_ref = repo.head().map_err(|e| GitError::Gix(Box::new(e)))?; + let head_commit_id = head_ref + .into_peeled_id() + .map_err(|e| GitError::Gix(Box::new(e)))?; + let head_commit_obj = repo + .find_object(head_commit_id.detach()) + .map_err(|e| GitError::Gix(Box::new(e)))?; + let head_commit = head_commit_obj + .try_into_commit() + .map_err(|_| GitError::BranchNotFound)?; + let mut head_tree = head_commit.tree().map_err(|e| GitError::Gix(Box::new(e)))?; // Compare git object content (both should be LFS pointers if using LFS) - if let Ok(current_content) = get_file_from_tree(repo, &head_tree, relative_path) + if let Ok(current_content) = get_file_from_tree(repo, &mut head_tree, relative_path) && default_file_content == current_content { return Ok(None); @@ -329,18 +366,22 @@ fn create_git_snapshot( fn get_file_from_tree( repo: &Repository, - tree: &git2::Tree<'_>, + tree: &mut gix::Tree<'_>, path: &Path, ) -> Result, GitError> { - let entry = tree.get_path(path)?; - let object = entry.to_object(repo)?; + let entry = tree + .peel_to_entry_by_path(path) + .map_err(|e| GitError::Gix(Box::new(e)))? + .ok_or(GitError::FileNotFound)?; - match object.kind() { - Some(ObjectType::Blob) => { - let blob = object.as_blob().ok_or(GitError::FileNotFound)?; - Ok(blob.content().to_vec()) - } - _ => Err(GitError::FileNotFound), + if entry.mode().is_blob() { + let object = repo + .find_object(entry.oid()) + .map_err(|e| GitError::Gix(Box::new(e)))?; + let blob = object.try_into_blob().map_err(|_| GitError::FileNotFound)?; + Ok(blob.data.to_vec()) + } else { + Err(GitError::FileNotFound) } } @@ -385,7 +426,9 @@ fn is_lfs_pointer(content: &[u8]) -> bool { fn get_github_repo_info(repo: &Repository) -> Option<(String, String)> { // Try to get the origin remote let remote = repo.find_remote("origin").ok()?; - let url = remote.url()?; + let url = remote.url(gix::remote::Direction::Fetch)?; + let url_str = url.to_bstring(); + let url = url_str.to_str().ok()?; // Parse GitHub URLs (both HTTPS and SSH) if let Some(caps) = parse_github_https_url(url) { From 95dd203eab9773309e14b4288bf023592d5278ab Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Tue, 14 Oct 2025 13:08:15 +0200 Subject: [PATCH 2/6] Fix inbox read --- src/native_loaders/git_loader.rs | 65 ++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/src/native_loaders/git_loader.rs b/src/native_loaders/git_loader.rs index 75a3caa..ede6b9d 100644 --- a/src/native_loaders/git_loader.rs +++ b/src/native_loaders/git_loader.rs @@ -71,9 +71,11 @@ impl GitLoader { impl LoadSnapshots for GitLoader { fn update(&mut self, ctx: &Context) { - if let Some(new_data) = self.inbox.read(ctx).last() { + + for new_data in self.inbox.read(ctx) { match new_data { Command::Snapshot(snapshot) => { + dbg!(&snapshot); self.snapshots.push(snapshot); sort_snapshots(&mut self.snapshots); } @@ -235,23 +237,45 @@ fn run_git_discovery(sender: &Sender, base_path: &Path) -> Result<(), GitError> gix::object::tree::diff::Action, Box, > { - // Check both old and new file paths (handles renames/moves) - let path = change.location(); - let path_str = path.to_str().unwrap_or(""); + // Check the file path + let file_path = change.location(); + let path_str = file_path.to_str().unwrap_or(""); let path_obj = Path::new(path_str); // Check if this is a PNG file - if let Some(extension) = path_obj.extension() { - if extension == "png" { - // Create snapshot for this changed PNG file - if let Ok(Some(snapshot)) = create_git_snapshot( - &repo, - &mut base_tree.clone(), - path_obj, - &github_repo_info, - &commit_sha, - ) { - sender.send(Command::Snapshot(snapshot)).ok(); + if let Some(extension) = path_obj.extension() + && extension == "png" + { + // Create snapshot for this changed PNG file + match base_commit.tree() { + Ok(base_tree) => { + match create_git_snapshot( + &repo, + &base_tree, + path_obj, + &github_repo_info, + &commit_sha, + ) { + Ok(Some(snapshot)) => { + println!("Created snapshot for {}", path_obj.display()); + sender.send(Command::Snapshot(snapshot)).ok(); + } + Ok(None) => { + dbg!("No snapshot created"); + log::info!("No snapshot created for {}", path_obj.display()); + } + Err(err) => { + dbg!(&err); + log::error!( + "Failed to create snapshot for {}: {}", + path_obj.display(), + err + ); + } + } + } + Err(err) => { + log::error!("Failed to get base tree: {}", err); } } } @@ -292,7 +316,7 @@ fn find_default_branch(repo: &Repository) -> Result { fn create_git_snapshot( repo: &Repository, - default_tree: &mut gix::Tree<'_>, + default_tree: &gix::Tree<'_>, relative_path: &Path, github_repo_info: &Option<(String, String)>, commit_sha: &str, @@ -326,10 +350,10 @@ fn create_git_snapshot( let head_commit = head_commit_obj .try_into_commit() .map_err(|_| GitError::BranchNotFound)?; - let mut head_tree = head_commit.tree().map_err(|e| GitError::Gix(Box::new(e)))?; + let head_tree = head_commit.tree().map_err(|e| GitError::Gix(Box::new(e)))?; // Compare git object content (both should be LFS pointers if using LFS) - if let Ok(current_content) = get_file_from_tree(repo, &mut head_tree, relative_path) + if let Ok(current_content) = get_file_from_tree(repo, &head_tree, relative_path) && default_file_content == current_content { return Ok(None); @@ -366,10 +390,11 @@ fn create_git_snapshot( fn get_file_from_tree( repo: &Repository, - tree: &mut gix::Tree<'_>, + tree: &gix::Tree<'_>, path: &Path, ) -> Result, GitError> { - let entry = tree + let mut tree_clone = tree.clone(); + let entry = tree_clone .peel_to_entry_by_path(path) .map_err(|e| GitError::Gix(Box::new(e)))? .ok_or(GitError::FileNotFound)?; From 5552532b711238529355c0bc2d7271be29cffc46 Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Wed, 15 Oct 2025 10:38:18 +0200 Subject: [PATCH 3/6] Fix images not found with different base path --- src/native_loaders/git_loader.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/native_loaders/git_loader.rs b/src/native_loaders/git_loader.rs index ede6b9d..670dab1 100644 --- a/src/native_loaders/git_loader.rs +++ b/src/native_loaders/git_loader.rs @@ -255,6 +255,7 @@ fn run_git_discovery(sender: &Sender, base_path: &Path) -> Result<(), GitError> path_obj, &github_repo_info, &commit_sha, + base_path, ) { Ok(Some(snapshot)) => { println!("Created snapshot for {}", path_obj.display()); @@ -320,6 +321,7 @@ fn create_git_snapshot( relative_path: &Path, github_repo_info: &Option<(String, String)>, commit_sha: &str, + base_path: &Path, ) -> Result, GitError> { // Skip files that are variants let file_name = relative_path @@ -380,10 +382,12 @@ fn create_git_snapshot( } }; + let full_path = base_path.join(relative_path); + Ok(Some(Snapshot { path: relative_path.to_path_buf(), old: Some(FileReference::Source(default_image_source)), // Default branch version as ImageSource - new: Some(FileReference::Path(relative_path.to_path_buf())), // Current working tree version + new: Some(FileReference::Path(full_path)), // Current working tree version with full path diff: None, // Always None for git mode })) } From 46aa6af3f08fe3ee1a0f81ce9576a1164276e915 Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Wed, 15 Oct 2025 10:52:16 +0200 Subject: [PATCH 4/6] Use anyhow --- src/native_loaders/git_loader.rs | 147 +++++++++---------------------- 1 file changed, 42 insertions(+), 105 deletions(-) diff --git a/src/native_loaders/git_loader.rs b/src/native_loaders/git_loader.rs index 670dab1..f5ff229 100644 --- a/src/native_loaders/git_loader.rs +++ b/src/native_loaders/git_loader.rs @@ -4,17 +4,16 @@ use eframe::egui::load::Bytes; use eframe::egui::{Context, ImageSource}; use egui_inbox::{UiInbox, UiInboxSender}; use gix::Repository; -use gix::bstr::ByteSlice; +use gix::bstr::ByteSlice as _; use octocrab::Octocrab; use std::borrow::Cow; -use std::fmt::Display; use std::path::{Path, PathBuf}; use std::str; use std::task::Poll; enum Command { Snapshot(Snapshot), - Error(GitError), + Error(anyhow::Error), Done, GitInfo(GitInfo), } @@ -75,12 +74,11 @@ impl LoadSnapshots for GitLoader { for new_data in self.inbox.read(ctx) { match new_data { Command::Snapshot(snapshot) => { - dbg!(&snapshot); self.snapshots.push(snapshot); sort_snapshots(&mut self.snapshots); } Command::Error(e) => { - self.state = Poll::Ready(Err(e.into())); + self.state = Poll::Ready(Err(e)); } Command::GitInfo(info) => { self.git_info = Some(info); @@ -119,45 +117,13 @@ impl LoadSnapshots for GitLoader { } } -#[derive(Debug)] -pub enum GitError { - RepoNotFound, - BranchNotFound, - FileNotFound, - Gix(Box), - IoError(std::io::Error), - PrUrlParseError, - NetworkError(String), -} - -impl Display for GitError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::RepoNotFound => write!(f, "Git repository not found"), - Self::BranchNotFound => write!(f, "Default branch not found"), - Self::FileNotFound => write!(f, "File not found in git tree"), - Self::Gix(err) => write!(f, "Git error: {err}"), - Self::IoError(err) => write!(f, "IO error: {err}"), - Self::PrUrlParseError => write!(f, "Failed to parse PR URL"), - Self::NetworkError(msg) => write!(f, "Network error: {msg}"), - } - } -} - -impl std::error::Error for GitError {} -impl From for GitError { - fn from(err: std::io::Error) -> Self { - Self::IoError(err) - } -} - -fn run_git_discovery(sender: &Sender, base_path: &Path) -> Result<(), GitError> { +fn run_git_discovery(sender: &Sender, base_path: &Path) -> anyhow::Result<()> { // Open git repository in current directory - let repo = gix::open(base_path).map_err(|_err| GitError::RepoNotFound)?; + let repo = gix::open(base_path).map_err(|e| anyhow::anyhow!("Git repository not found: {}", e))?; // Get current branch - let head = repo.head().map_err(|e| GitError::Gix(Box::new(e)))?; + let head = repo.head()?; let current_branch = head .referent_name() .and_then(|n| n.shorten().as_bstr().to_str().ok()) @@ -190,29 +156,19 @@ fn run_git_discovery(sender: &Sender, base_path: &Path) -> Result<(), GitError> } // Get the merge base between current branch and default branch - let head_ref = repo.head().map_err(|e| GitError::Gix(Box::new(e)))?; - let head_commit_id = head_ref - .into_peeled_id() - .map_err(|e| GitError::Gix(Box::new(e)))?; - let head_commit_obj = repo - .find_object(head_commit_id.detach()) - .map_err(|e| GitError::Gix(Box::new(e)))?; + let head_ref = repo.head()?; + let head_commit_id = head_ref.into_peeled_id()?; + let head_commit_obj = repo.find_object(head_commit_id.detach())?; let head_commit = head_commit_obj .try_into_commit() - .map_err(|_| GitError::BranchNotFound)?; - - let default_ref = repo - .find_reference(&format!("refs/heads/{}", default_branch)) - .map_err(|e| GitError::Gix(Box::new(e)))?; - let default_commit_id = default_ref - .into_fully_peeled_id() - .map_err(|e| GitError::Gix(Box::new(e)))?; - let default_commit_obj = repo - .find_object(default_commit_id.detach()) - .map_err(|e| GitError::Gix(Box::new(e)))?; + .map_err(|_| anyhow::anyhow!("Failed to get commit from HEAD"))?; + + let default_ref = repo.find_reference(&format!("refs/heads/{default_branch}"))?; + let default_commit_id = default_ref.into_fully_peeled_id()?; + let default_commit_obj = repo.find_object(default_commit_id.detach())?; let default_commit = default_commit_obj .try_into_commit() - .map_err(|_| GitError::BranchNotFound)?; + .map_err(|_| anyhow::anyhow!("Failed to get commit from default branch"))?; // Find merge base - for now, just use the default branch commit as the base // This is a simplification but will work for the common case @@ -223,14 +179,12 @@ fn run_git_discovery(sender: &Sender, base_path: &Path) -> Result<(), GitError> let commit_sha = base_commit.id.to_string(); // Get current HEAD tree for comparison - let head_tree = head_commit.tree().map_err(|e| GitError::Gix(Box::new(e)))?; + let head_tree = head_commit.tree()?; - let base_tree = base_commit.tree().map_err(|e| GitError::Gix(Box::new(e)))?; + let base_tree = base_commit.tree()?; // Use gix diff to find changed PNG files between merge base and current HEAD - base_tree - .changes() - .map_err(|e| GitError::Gix(Box::new(e)))? + base_tree.changes()? .for_each_to_obtain_tree( &head_tree, |change: gix::object::tree::diff::Change<'_, '_, '_>| -> Result< @@ -258,41 +212,33 @@ fn run_git_discovery(sender: &Sender, base_path: &Path) -> Result<(), GitError> base_path, ) { Ok(Some(snapshot)) => { - println!("Created snapshot for {}", path_obj.display()); sender.send(Command::Snapshot(snapshot)).ok(); } Ok(None) => { - dbg!("No snapshot created"); log::info!("No snapshot created for {}", path_obj.display()); } Err(err) => { - dbg!(&err); - log::error!( - "Failed to create snapshot for {}: {}", - path_obj.display(), - err - ); + log::error!("Failed to create snapshot for {}: {err}", path_obj.display()); } } } Err(err) => { - log::error!("Failed to get base tree: {}", err); + log::error!("Failed to get base tree: {err}"); } } } Ok(gix::object::tree::diff::Action::Continue) }, - ) - .map_err(|e| GitError::Gix(Box::new(e)))?; + )?; Ok(()) } -fn find_default_branch(repo: &Repository) -> Result { +fn find_default_branch(repo: &Repository) -> anyhow::Result { // Try common default branch names for branch_name in ["main", "master"] { if repo - .find_reference(&format!("refs/heads/{}", branch_name)) + .find_reference(&format!("refs/heads/{branch_name}")) .is_ok() { return Ok(branch_name.to_owned()); @@ -300,19 +246,15 @@ fn find_default_branch(repo: &Repository) -> Result { } // Fall back to first branch found - let references = repo.references().map_err(|e| GitError::Gix(Box::new(e)))?; + let references = repo.references()?; - for reference in (references - .prefixed("refs/heads/") - .map_err(|e| GitError::Gix(Box::new(e)))?) - .flatten() - { + for reference in references.prefixed("refs/heads/")?.flatten() { if let Ok(name) = reference.name().shorten().to_str() { return Ok(name.to_owned()); } } - Err(GitError::BranchNotFound) + anyhow::bail!("No default branch found") } fn create_git_snapshot( @@ -322,12 +264,12 @@ fn create_git_snapshot( github_repo_info: &Option<(String, String)>, commit_sha: &str, base_path: &Path, -) -> Result, GitError> { +) -> anyhow::Result> { // Skip files that are variants let file_name = relative_path .file_name() .and_then(|n| n.to_str()) - .ok_or(GitError::FileNotFound)?; + .ok_or_else(|| anyhow::anyhow!("Invalid file path"))?; if file_name.ends_with(".old.png") || file_name.ends_with(".new.png") @@ -342,17 +284,13 @@ fn create_git_snapshot( }; // Get the current file from the current branch's tree to compare git objects properly - let head_ref = repo.head().map_err(|e| GitError::Gix(Box::new(e)))?; - let head_commit_id = head_ref - .into_peeled_id() - .map_err(|e| GitError::Gix(Box::new(e)))?; - let head_commit_obj = repo - .find_object(head_commit_id.detach()) - .map_err(|e| GitError::Gix(Box::new(e)))?; + let head_ref = repo.head()?; + let head_commit_id = head_ref.into_peeled_id()?; + let head_commit_obj = repo.find_object(head_commit_id.detach())?; let head_commit = head_commit_obj .try_into_commit() - .map_err(|_| GitError::BranchNotFound)?; - let head_tree = head_commit.tree().map_err(|e| GitError::Gix(Box::new(e)))?; + .map_err(|_| anyhow::anyhow!("Failed to get commit from HEAD"))?; + let head_tree = head_commit.tree()?; // Compare git object content (both should be LFS pointers if using LFS) if let Ok(current_content) = get_file_from_tree(repo, &head_tree, relative_path) @@ -396,21 +334,20 @@ fn get_file_from_tree( repo: &Repository, tree: &gix::Tree<'_>, path: &Path, -) -> Result, GitError> { +) -> anyhow::Result> { let mut tree_clone = tree.clone(); let entry = tree_clone - .peel_to_entry_by_path(path) - .map_err(|e| GitError::Gix(Box::new(e)))? - .ok_or(GitError::FileNotFound)?; + .peel_to_entry_by_path(path)? + .ok_or_else(|| anyhow::anyhow!("File not found in tree"))?; if entry.mode().is_blob() { - let object = repo - .find_object(entry.oid()) - .map_err(|e| GitError::Gix(Box::new(e)))?; - let blob = object.try_into_blob().map_err(|_| GitError::FileNotFound)?; - Ok(blob.data.to_vec()) + let object = repo.find_object(entry.oid())?; + let blob = object + .try_into_blob() + .map_err(|_| anyhow::anyhow!("Entry is not a blob"))?; + Ok(blob.data.clone()) } else { - Err(GitError::FileNotFound) + anyhow::bail!("Path is not a file") } } From c1cd8bbbe9b1d25d9c8f0efc50c55ccfd99f3a9f Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Wed, 15 Oct 2025 10:58:35 +0200 Subject: [PATCH 5/6] Improved errors --- src/native_loaders/git_loader.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/native_loaders/git_loader.rs b/src/native_loaders/git_loader.rs index f5ff229..c27cf39 100644 --- a/src/native_loaders/git_loader.rs +++ b/src/native_loaders/git_loader.rs @@ -120,7 +120,7 @@ impl LoadSnapshots for GitLoader { fn run_git_discovery(sender: &Sender, base_path: &Path) -> anyhow::Result<()> { // Open git repository in current directory - let repo = gix::open(base_path).map_err(|e| anyhow::anyhow!("Git repository not found: {}", e))?; + let repo = gix::open(base_path).map_err(|e| anyhow::anyhow!("Git repository not found: {e}"))?; // Get current branch let head = repo.head()?; @@ -161,14 +161,14 @@ fn run_git_discovery(sender: &Sender, base_path: &Path) -> anyhow::Result<()> { let head_commit_obj = repo.find_object(head_commit_id.detach())?; let head_commit = head_commit_obj .try_into_commit() - .map_err(|_| anyhow::anyhow!("Failed to get commit from HEAD"))?; + .map_err(|e| anyhow::anyhow!("Failed to get commit from HEAD: {e:?}"))?; let default_ref = repo.find_reference(&format!("refs/heads/{default_branch}"))?; let default_commit_id = default_ref.into_fully_peeled_id()?; let default_commit_obj = repo.find_object(default_commit_id.detach())?; let default_commit = default_commit_obj .try_into_commit() - .map_err(|_| anyhow::anyhow!("Failed to get commit from default branch"))?; + .map_err(|e| anyhow::anyhow!("Failed to get commit from default branch: {e:?}"))?; // Find merge base - for now, just use the default branch commit as the base // This is a simplification but will work for the common case @@ -289,7 +289,7 @@ fn create_git_snapshot( let head_commit_obj = repo.find_object(head_commit_id.detach())?; let head_commit = head_commit_obj .try_into_commit() - .map_err(|_| anyhow::anyhow!("Failed to get commit from HEAD"))?; + .map_err(|e| anyhow::anyhow!("Failed to get commit from HEAD: {e:?}"))?; let head_tree = head_commit.tree()?; // Compare git object content (both should be LFS pointers if using LFS) @@ -344,7 +344,7 @@ fn get_file_from_tree( let object = repo.find_object(entry.oid())?; let blob = object .try_into_blob() - .map_err(|_| anyhow::anyhow!("Entry is not a blob"))?; + .map_err(|e| anyhow::anyhow!("Entry is not a blob: {e:?}"))?; Ok(blob.data.clone()) } else { anyhow::bail!("Path is not a file") From 4689abdf4cd101df5d28d3a2c5016e95f9f77341 Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Wed, 15 Oct 2025 11:04:29 +0200 Subject: [PATCH 6/6] Fmt --- src/native_loaders/git_loader.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/native_loaders/git_loader.rs b/src/native_loaders/git_loader.rs index c27cf39..a7d525d 100644 --- a/src/native_loaders/git_loader.rs +++ b/src/native_loaders/git_loader.rs @@ -70,7 +70,6 @@ impl GitLoader { impl LoadSnapshots for GitLoader { fn update(&mut self, ctx: &Context) { - for new_data in self.inbox.read(ctx) { match new_data { Command::Snapshot(snapshot) => { @@ -117,10 +116,10 @@ impl LoadSnapshots for GitLoader { } } - fn run_git_discovery(sender: &Sender, base_path: &Path) -> anyhow::Result<()> { // Open git repository in current directory - let repo = gix::open(base_path).map_err(|e| anyhow::anyhow!("Git repository not found: {e}"))?; + let repo = + gix::open(base_path).map_err(|e| anyhow::anyhow!("Git repository not found: {e}"))?; // Get current branch let head = repo.head()?; @@ -326,7 +325,7 @@ fn create_git_snapshot( path: relative_path.to_path_buf(), old: Some(FileReference::Source(default_image_source)), // Default branch version as ImageSource new: Some(FileReference::Path(full_path)), // Current working tree version with full path - diff: None, // Always None for git mode + diff: None, // Always None for git mode })) }