From d3d0be02bff30335e0e90bab50ea482bf83a4092 Mon Sep 17 00:00:00 2001 From: Seongmin Lee Date: Mon, 29 Jun 2026 08:41:37 +0000 Subject: [PATCH] gitmirror: introduce gitmirror gitmirror is just another name of knotmirror/v2. Current Knotmirror will be gradually replaced by gitmirror except the knot-relay part. Signed-off-by: Seongmin Lee --- .gitattributes | 2 ++ Cargo.lock | 1478 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------- Cargo.toml | 7 ++++++- docker-compose.yml | 15 +++++++++++++++ flake.nix | 3 +++ gitmirror/Cargo.toml | 30 ++++++++++++++++++++++++++++++ gitmirror/README.md | 4 ++++ gitmirror/buf.gen.yaml | 12 ++++++++++++ gitmirror/buf.yaml | 3 +++ gitmirror/build.rs | 4 ++++ gitmirror/example/client.go | 42 ++++++++++++++++++++++++++++++++++++++++++ gitmirror/proto/gen/gitmirror.pb.go | 1119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ gitmirror/proto/gen/gitmirror_grpc.pb.go | 247 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ gitmirror/proto/gitmirror/v1/gitmirror.proto | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ gitmirror/src/diff.rs | 370 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ gitmirror/src/main.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++ gitmirror/src/protocol.rs | 3 +++ gitmirror/src/service.rs | 362 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ localinfra/Caddyfile | 10 ++++++++++ localinfra/gitmirror.Dockerfile | 29 +++++++++++++++++++++++++++++ 20 file(s) changed, 3890 insertion(s)(+), 30 deletion(s)(-) diff --git a/.gitattributes b/.gitattributes --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,5 @@ api/tangled/** linguist-generated -diff api/tangled/*_ext.go -linguist-generated diff flake.lock -diff +Cargo.lock -diff +gitmirror/proto/gen/** linguist-generated -diff diff --git a/Cargo.lock b/Cargo.lock --- a/Cargo.lock +++ b/Cargo.lock @@ -149,6 +149,12 @@ "rustversion", ] [[package]] +name = "arrayvec" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02882884d3e1bc524fb12c79f107f6ad0e1cfd498c536ffb494301740995dfe" + +[[package]] name = "assert-json-diff" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -311,6 +317,12 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" @@ -331,6 +343,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", ] [[package]] @@ -648,6 +669,17 @@ "tinyvec", ] [[package]] +name = "bstr" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cee35f73844aa3014bb606320a6c1f010249dbdf43342fe54b5a4f6a8ed4b79" +dependencies = [ + "memchr", + "regex-automata", + "serde_core", +] + +[[package]] name = "bumpalo" version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -667,6 +699,12 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" dependencies = [ "serde", ] + +[[package]] +name = "bytesize" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49e78e506b9d7633710dab98996f22f95f3d0f488e8f1aa162830556ed9fc14d" [[package]] name = "cast" @@ -824,6 +862,15 @@ name = "clap_lex" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "clru" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "197fd99cb113a8d5d9b6376f3aa817f32c1078f2343b714fff7d2ca44fdf67d5" +dependencies = [ + "hashbrown 0.16.1", +] [[package]] name = "cobs" @@ -938,6 +985,15 @@ "libc", ] [[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] name = "crc32fast" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1048,6 +1104,15 @@ "typenum", ] [[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] name = "darling" version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1146,6 +1211,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" [[package]] +name = "defmt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e524506490a1953d237cb87b1cfc1e46f88c18f10a22dfe0f507dc6bfc7f7f" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0a27770e9c8f719a79d8b638281f4d828f77d8fd61e0bd94451b9b85e576a0b" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror 2.0.18", +] + +[[package]] name = "der" version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1199,10 +1296,20 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "const-oid", - "crypto-common", + "crypto-common 0.1.6", "subtle", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "crypto-common 0.2.2", ] [[package]] @@ -1223,13 +1330,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" [[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] name = "ecdsa" version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", - "digest", + "digest 0.10.7", "elliptic-curve", "rfc6979", "signature", @@ -1250,7 +1363,7 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "digest", + "digest 0.10.7", "ff", "generic-array", "group", @@ -1317,6 +1430,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afc2bd4d5a73106dd53d10d73d3401c2f32730ba2c0b93ddb888a8983680471" [[package]] +name = "faster-hex" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7223ae2d2f179b803433d9c830478527e92b8117eab39460edae7f1614d9fb73" +dependencies = [ + "heapless 0.8.0", + "serde", +] + +[[package]] name = "fastrand" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1333,6 +1456,16 @@ "subtle", ] [[package]] +name = "filetime" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" +dependencies = [ + "cfg-if", + "libc", +] + +[[package]] name = "find-msvc-tools" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1583,6 +1716,948 @@ "wasip3", ] [[package]] +name = "gitmirror" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "gix", + "line-numbers", + "prost", + "prost-types", + "rustc-hash", + "tempfile", + "tokio", + "tokio-stream", + "tonic", + "tonic-prost", + "tonic-prost-build", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "gix" +version = "0.84.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae54ae0ebd1a5a3c3f8d95dd3b5ca6e63f4fed9bfd585e13801a97d7bde8f9ce" +dependencies = [ + "gix-actor", + "gix-archive", + "gix-attributes", + "gix-blame", + "gix-command", + "gix-commitgraph", + "gix-config", + "gix-credentials", + "gix-date", + "gix-diff", + "gix-dir", + "gix-discover", + "gix-error", + "gix-features", + "gix-filter", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-hashtable", + "gix-ignore", + "gix-index", + "gix-lock", + "gix-mailmap", + "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-status", + "gix-submodule", + "gix-tempfile", + "gix-trace", + "gix-traverse", + "gix-url", + "gix-utils", + "gix-validate", + "gix-worktree", + "gix-worktree-state", + "gix-worktree-stream", + "nonempty", + "parking_lot", + "regex", + "signal-hook", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-actor" +version = "0.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bc998b8f746dda8565450d08a63b792ced9165d8c27a1ed3f02799ec6a7820f" +dependencies = [ + "bstr", + "gix-date", + "gix-error", +] + +[[package]] +name = "gix-archive" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16909cacc78936ab96f6c3be08379d0a2e88bfa3a7527972d2ed75c7517ef31e" +dependencies = [ + "bstr", + "gix-date", + "gix-error", + "gix-object", + "gix-worktree-stream", +] + +[[package]] +name = "gix-attributes" +version = "0.33.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39b40888d0ed415c0744a6cdc61eebf0304c9d26ab726725b718443c322e5ba4" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "gix-quote", + "gix-trace", + "kstring", + "smallvec", + "thiserror 2.0.18", + "unicode-bom", +] + +[[package]] +name = "gix-bitmap" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ebef0c26ad305747649e727bbcd56a7b7910754eb7cea88f6dff6f93c51283" +dependencies = [ + "gix-error", +] + +[[package]] +name = "gix-blame" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d39a0c14af94c2edaa5eefe06d5ef2cdea55316ae9a9321314288e3f55fa4c0" +dependencies = [ + "gix-commitgraph", + "gix-date", + "gix-diff", + "gix-error", + "gix-hash", + "gix-object", + "gix-revwalk", + "gix-trace", + "gix-traverse", + "gix-worktree", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-chunk" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9faee47943b638e58ddd5e275a4906ad3e4b6c8584f1d41bd18ab9032ec52afb" +dependencies = [ + "gix-error", +] + +[[package]] +name = "gix-command" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00706d4fef135ef4b01680d5218c6ee40cda8baf697b864296cbc887d19118f6" +dependencies = [ + "bstr", + "gix-path", + "gix-quote", + "gix-trace", + "shell-words", +] + +[[package]] +name = "gix-commitgraph" +version = "0.37.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f675d0df484a7f6a47e64bd6f311af489d947c0323b0564f36d14f3d7762abb" +dependencies = [ + "bstr", + "gix-chunk", + "gix-error", + "gix-hash", + "memmap2", + "nonempty", +] + +[[package]] +name = "gix-config" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2372d4b49ca28431e7d150cab9d25edc1890f0184bd57eb0e917c7799e63de" +dependencies = [ + "bstr", + "gix-config-value", + "gix-features", + "gix-glob", + "gix-path", + "gix-ref", + "gix-sec", + "smallvec", + "thiserror 2.0.18", + "unicode-bom", +] + +[[package]] +name = "gix-config-value" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed42168329552f6c2e5df09665c104199d45d84bedb53683738a49b57fe1baab" +dependencies = [ + "bitflags 2.11.1", + "bstr", + "gix-path", + "libc", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-credentials" +version = "0.38.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40cd22f0dd71988be12d6e78b1709de2370e1957c5f107ff31e56caeba3745d" +dependencies = [ + "bstr", + "gix-command", + "gix-config-value", + "gix-date", + "gix-path", + "gix-prompt", + "gix-sec", + "gix-trace", + "gix-url", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-date" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d63f9e28b59ddeb1a1eb9e5cf986a9222b5d484947445edbc20473939cc7fd0" +dependencies = [ + "bstr", + "gix-error", + "itoa", + "jiff", +] + +[[package]] +name = "gix-diff" +version = "0.64.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b6d9528f32d94cef2edf39a1ac01fe5a0fc44ddbb18d9e44099936047c3302b" +dependencies = [ + "bstr", + "gix-attributes", + "gix-command", + "gix-filter", + "gix-fs", + "gix-hash", + "gix-imara-diff", + "gix-index", + "gix-object", + "gix-path", + "gix-pathspec", + "gix-tempfile", + "gix-trace", + "gix-traverse", + "gix-worktree", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-dir" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bb2a53a6fd917ec499ed0bfb5b6887de7a15bd79197dcea7c987938749a9f1" +dependencies = [ + "bstr", + "gix-discover", + "gix-fs", + "gix-ignore", + "gix-index", + "gix-object", + "gix-path", + "gix-pathspec", + "gix-trace", + "gix-utils", + "gix-worktree", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-discover" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77bacdd12b7879d2178a80c58c2f319995e4654e1a7a23e3181e5c8a12b824f7" +dependencies = [ + "bstr", + "dunce", + "gix-fs", + "gix-path", + "gix-ref", + "gix-sec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-error" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57831e199be480af90dcd7e459abed8a174c09ec9a6e2cc8f7ca6c54598b06b" +dependencies = [ + "bstr", +] + +[[package]] +name = "gix-features" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1849ae154d38bc403185be14fa871e38e3c93ee606875d94e207fdb9fba52dbc" +dependencies = [ + "bytes", + "bytesize", + "crc32fast", + "crossbeam-channel", + "gix-path", + "gix-trace", + "gix-utils", + "libc", + "once_cell", + "parking_lot", + "prodash", + "thiserror 2.0.18", + "walkdir", + "zlib-rs", +] + +[[package]] +name = "gix-filter" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecf74b7d16f6694ce4a3049074c41be0c7987105743674f1671807bd6dce09fa" +dependencies = [ + "bstr", + "encoding_rs", + "gix-attributes", + "gix-command", + "gix-hash", + "gix-object", + "gix-packetline", + "gix-path", + "gix-quote", + "gix-trace", + "gix-utils", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-fs" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cdff46db8798e47e2f727d84b9379aac5add3dd3d9d0b07bb4d7d5d640771fe" +dependencies = [ + "bstr", + "fastrand", + "gix-features", + "gix-path", + "gix-utils", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-glob" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1fcb8ef5b16bcf874abe9b68d8abb3c0493c876d367ab824151f30a0f3f3756" +dependencies = [ + "bitflags 2.11.1", + "bstr", + "gix-features", + "gix-path", +] + +[[package]] +name = "gix-hash" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0926d3819c837750b4e03c7754901e73f68b8c9b690753a6372a1bed4eedce" +dependencies = [ + "faster-hex", + "gix-features", + "sha1-checked", + "sha2 0.11.0", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-hashtable" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e261d54091f0d1c729bc83f54548c071bdec60a697de1e58e88bdfd7a99d24e" +dependencies = [ + "gix-hash", + "hashbrown 0.17.1", + "parking_lot", +] + +[[package]] +name = "gix-ignore" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d491bab9bf2c9f341dc754f425c31d5d3f63aca615312167b82e1deeaca97d8d" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "gix-trace", + "unicode-bom", +] + +[[package]] +name = "gix-imara-diff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b305d85504de270ad3525d726a6b69cc59ee7b2269b014387651107ab9f0755b" +dependencies = [ + "bstr", + "hashbrown 0.17.1", +] + +[[package]] +name = "gix-index" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e6b28cc592dc753adb58302bb14a64e412ee591a3bec77aa4df87bff74fa80d" +dependencies = [ + "bitflags 2.11.1", + "bstr", + "filetime", + "fnv", + "gix-bitmap", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-traverse", + "gix-utils", + "gix-validate", + "hashbrown 0.17.1", + "itoa", + "libc", + "memmap2", + "rustix", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-lock" +version = "23.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c9dedd9e90b0d47624d2ed241d394e09294118364e87b9b7e5f1fe755f3c2c" +dependencies = [ + "gix-tempfile", + "gix-utils", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-mailmap" +version = "0.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "195fd20808055824531be2fd0d34136d900e5fbca3ffb0a3c07e8beeefb9c828" +dependencies = [ + "bstr", + "gix-actor", + "gix-date", + "gix-error", +] + +[[package]] +name = "gix-merge" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7543e1eceb25fbbd1a29459c794148d8bafc1c77555eb386617a2d99b5371971" +dependencies = [ + "bstr", + "gix-command", + "gix-diff", + "gix-filter", + "gix-fs", + "gix-hash", + "gix-imara-diff", + "gix-index", + "gix-object", + "gix-path", + "gix-quote", + "gix-revision", + "gix-revwalk", + "gix-tempfile", + "gix-trace", + "gix-worktree", + "nonempty", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-negotiate" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "890c936a215bae25818c076cb881cb2e54d2c66ba947ba58b8dd47cff921bf55" +dependencies = [ + "bitflags 2.11.1", + "gix-commitgraph", + "gix-date", + "gix-hash", + "gix-object", + "gix-revwalk", +] + +[[package]] +name = "gix-object" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5cd857e29429c7213bdef3f5aef83f8cc124774fe8ae0d27b1607d218d6d525" +dependencies = [ + "bstr", + "gix-actor", + "gix-date", + "gix-features", + "gix-hash", + "gix-hashtable", + "gix-utils", + "gix-validate", + "itoa", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-odb" +version = "0.81.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d004c32858b1556f2d7874405edb3c97dc78fc09beaa87d57bb077ee2858a7d" +dependencies = [ + "arc-swap", + "gix-features", + "gix-fs", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-pack", + "gix-path", + "gix-quote", + "memmap2", + "parking_lot", + "tempfile", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-pack" +version = "0.71.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e43626f2a27d1033674ec1a196b845614231e6bbd949d5e21c133045ff56b174" +dependencies = [ + "clru", + "gix-chunk", + "gix-error", + "gix-features", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-path", + "memmap2", + "smallvec", + "thiserror 2.0.18", + "uluru", +] + +[[package]] +name = "gix-packetline" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b217dd0ee0c4021ecf169a4a519b1b4f80d15e3f3765f3dc466223dc0ac891d7" +dependencies = [ + "bstr", + "faster-hex", + "gix-trace", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-path" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa6ac14cd14939ea94a496ce7460daa6511c09f5b84757e9cfc6f9c8d0f93a6" +dependencies = [ + "bstr", + "gix-trace", + "gix-validate", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-pathspec" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3050783b41ee11511e1e8fb35623df81806194f4030395f14f48ea37c2798c9f" +dependencies = [ + "bitflags 2.11.1", + "bstr", + "gix-attributes", + "gix-config-value", + "gix-glob", + "gix-path", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-prompt" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee604d7746080ae7e1023bf47204bcc2c5f307bfbe2306a3c90b1bfd1a2c6d8" +dependencies = [ + "gix-command", + "gix-config-value", + "parking_lot", + "rustix", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-protocol" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51dea3acb390707ab868f1f9584f18449eb95d869deffae96768e47d303595ee" +dependencies = [ + "bstr", + "gix-date", + "gix-features", + "gix-hash", + "gix-ref", + "gix-shallow", + "gix-transport", + "gix-utils", + "maybe-async", + "nonempty", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-quote" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e541fc33cc2b783b7979040d445a0c86a2eca747c8faea4ca84230d06ae6ef" +dependencies = [ + "bstr", + "gix-error", + "gix-utils", +] + +[[package]] +name = "gix-ref" +version = "0.64.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c04f64c37eb7e6feb73c7060f8dc6f381cc5de5d53249bfd450bc48a86b2e8b" +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.18", +] + +[[package]] +name = "gix-refspec" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b216ae06ec74b5f24ad0142026a997fb0a935b7410eaf9c1616fc3f0e6c5a6d3" +dependencies = [ + "bstr", + "gix-error", + "gix-glob", + "gix-hash", + "gix-revision", + "gix-validate", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-revision" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b47c88884dd3c1a19a39da19d10211fcdea2809aadc86869b6e824a1774340f" +dependencies = [ + "bitflags 2.11.1", + "bstr", + "gix-commitgraph", + "gix-date", + "gix-error", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-revwalk", + "gix-trace", + "nonempty", +] + +[[package]] +name = "gix-revwalk" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85f5756abffe0917827aac683b13684ed99875bc398fa1f9b8f479b0681ef9e6" +dependencies = [ + "gix-commitgraph", + "gix-date", + "gix-error", + "gix-hash", + "gix-hashtable", + "gix-object", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-sec" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8519976e4c7e486270740a5400369f37940779b80bd1377d94cfa1125d01b3" +dependencies = [ + "bitflags 2.11.1", + "gix-path", + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "gix-shallow" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a292fc2fe548c5dfa575479d16b445b0ddf1dd2f56f1fec6aed386f82553cd97" +dependencies = [ + "bstr", + "gix-hash", + "gix-lock", + "nonempty", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-status" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22042e385d28a34275e029d98f4970285045be14b9073658ca897923f2ed8700" +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.18", +] + +[[package]] +name = "gix-submodule" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3059890ef054066c22a94bfc6a3eaba0d806aedcd630a0bc9e5783fd88884781" +dependencies = [ + "bstr", + "gix-config", + "gix-path", + "gix-pathspec", + "gix-refspec", + "gix-url", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-tempfile" +version = "23.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ef60812443484e67bf84e444cc71b4c78ae62deb822221774a4fa0c57fdb17f" +dependencies = [ + "dashmap", + "gix-fs", + "libc", + "parking_lot", + "signal-hook", + "signal-hook-registry", + "tempfile", +] + +[[package]] +name = "gix-trace" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44dc45eae785c0eb14173e0f152e6e224dcf4d45b6a6999a3aed22af541ad678" + +[[package]] +name = "gix-transport" +version = "0.57.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186874f7ad1fb2f9a2f2aa9c2dabc7f9dd087bef74c1a0eee2b4a9cf0248fcb3" +dependencies = [ + "bstr", + "gix-command", + "gix-features", + "gix-packetline", + "gix-quote", + "gix-sec", + "gix-url", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-traverse" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8de590ecc86a3b2870665f2288324fa9f7f8672c7fc2d4e020fdd81cd1f7aed" +dependencies = [ + "bitflags 2.11.1", + "gix-commitgraph", + "gix-date", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-revwalk", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-url" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bb01ec69d55e82ccb7a19e264501ead4e6aac38463a8cebfdd81e22bb67ab2" +dependencies = [ + "bstr", + "gix-path", + "percent-encoding", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-utils" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66c50966184123caf580ffa64e28031a878597f1c7fceb8fe19566c38eb1b771" +dependencies = [ + "bstr", + "fastrand", + "unicode-normalization", +] + +[[package]] +name = "gix-validate" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bc6fc771c4063ba7cd2f47b91fb6076251c6a823b64b7fe7b8874b0fe4afae3" +dependencies = [ + "bstr", +] + +[[package]] +name = "gix-worktree" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cef414ed275e8407cd5d53d301e83be19700b0dd3f859d2434417b58f454a2d1" +dependencies = [ + "bstr", + "gix-attributes", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-ignore", + "gix-index", + "gix-object", + "gix-path", + "gix-validate", +] + +[[package]] +name = "gix-worktree-state" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bffae8b3ca258fdd50370cd51f06deb4c76a3b43db3868bc28dde45ffa77d69" +dependencies = [ + "bstr", + "gix-features", + "gix-filter", + "gix-fs", + "gix-index", + "gix-object", + "gix-path", + "gix-worktree", + "io-close", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-worktree-stream" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25e9ed30100c63f7590bc581c225e53f731a53e06aa79a245739c07f7dcc557" +dependencies = [ + "gix-attributes", + "gix-error", + "gix-features", + "gix-filter", + "gix-fs", + "gix-hash", + "gix-object", + "gix-path", + "gix-traverse", + "parking_lot", +] + +[[package]] name = "group" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1633,6 +2708,15 @@ "byteorder", ] [[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" @@ -1669,6 +2753,11 @@ name = "hashbrown" version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] [[package]] name = "heapless" @@ -1677,10 +2766,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" dependencies = [ "atomic-polyfill", - "hash32", + "hash32 0.2.1", "rustc_version", "serde", "spin 0.9.8", + "stable_deref_trait", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32 0.3.1", "stable_deref_trait", ] @@ -1714,7 +2813,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -1769,6 +2868,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] +name = "human_format" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaec953f16e5bcf6b8a3cb3aa959b17e5577dbd2693e94554c462c08be22624b" + +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "typenum", +] + +[[package]] name = "hyper" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1807,6 +2921,19 @@ "webpki-roots 1.0.7", ] [[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] name = "hyper-util" version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1992,6 +3119,16 @@ "rustversion", ] [[package]] +name = "io-close" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cadcf447f06744f8ce713d2d6239bb5bde2c357a452397a9ed90c625da390bc" +dependencies = [ + "libc", + "winapi", +] + +[[package]] name = "ipld-core" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2126,13 +3263,55 @@ "serde_json", "serde_path_to_error", "serde_repr", "serde_with", - "sha2", + "sha2 0.10.9", "syn", "thiserror 2.0.18", "unicode-segmentation", ] [[package]] +name = "jiff" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34f877a98676d2fb664698d74cc6a51ce6c484ce8c770f05d0108ec9090aeb46" +dependencies = [ + "defmt", + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-link", +] + +[[package]] +name = "jiff-static" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0666b5ab5ecaca213fc2a85b8c0083d9004e84ee2d5f9a7e0017aaf50986f25f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c900ef84826f1338a557697dc8fc601df9ca9af4ac137c7fb61d4c6f2dfd3076" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + +[[package]] name = "jobserver" version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2163,7 +3342,16 @@ dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", - "sha2", + "sha2 0.10.9", +] + +[[package]] +name = "kstring" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1" +dependencies = [ + "static_assertions", ] [[package]] @@ -2199,6 +3387,12 @@ name = "libc" version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "line-numbers" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ab6409a2f9ffae158ae823a012a7d7d68386663a14bedc260ca66eacf65c2c" [[package]] name = "linux-raw-sys" @@ -2301,6 +3495,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] +name = "maybe-async" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "746873a384ad60adc5db74471dfaba74bd278afbdcfd81db93fafcdfc8b5ca0c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "measure_time" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2464,7 +3669,7 @@ version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ - "bitflags", + "bitflags 2.11.1", "cfg-if", "cfg_aliases", "libc", @@ -2482,6 +3687,12 @@ "minimal-lexical", ] [[package]] +name = "nonempty" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9737e026353e5cd0736f98eddae28665118eb6f6600902a7f50db585621fecb6" + +[[package]] name = "nu-ansi-term" version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2614,7 +3825,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -2779,6 +3990,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + +[[package]] name = "postcard" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2787,7 +4007,7 @@ dependencies = [ "cobs", "embedded-io 0.4.0", "embedded-io 0.6.1", - "heapless", + "heapless 0.7.17", "serde", ] @@ -2835,6 +4055,28 @@ "elliptic-curve", ] [[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "proc-macro2" version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2857,10 +4099,21 @@ "yansi", ] [[package]] +name = "prodash" +version = "31.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "962200e2d7d551451297d9fdce85138374019ada198e30ea9ede38034e27604c" +dependencies = [ + "bytesize", + "human_format", + "parking_lot", +] + +[[package]] name = "prost" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" dependencies = [ "bytes", "prost-derive", @@ -2880,6 +4133,8 @@ "petgraph", "prettyplease", "prost", "prost-types", + "pulldown-cmark", + "pulldown-cmark-to-cmark", "regex", "syn", "tempfile", @@ -2887,9 +4142,9 @@ ] [[package]] name = "prost-derive" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", "itertools 0.14.0", @@ -2967,14 +4222,34 @@ ] [[package]] name = "prost-types" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7" +checksum = "f94967dc7688f3054c7fac87473ffae4cc4c3904800e2d9f5b857246d8963b0a" dependencies = [ "prost", ] [[package]] +name = "pulldown-cmark" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e" +dependencies = [ + "bitflags 2.11.1", + "memchr", + "unicase", +] + +[[package]] +name = "pulldown-cmark-to-cmark" +version = "22.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50793def1b900256624a709439404384204a5dc3a6ec580281bfaac35e882e90" +dependencies = [ + "pulldown-cmark", +] + +[[package]] name = "quick_cache" version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3147,7 +4422,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags", + "bitflags 2.11.1", ] [[package]] @@ -3304,7 +4579,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags", + "bitflags 2.11.1", "errno", "libc", "linux-raw-sys", @@ -3445,7 +4720,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags", + "bitflags 2.11.1", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -3639,8 +4914,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha1-checked" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89f599ac0c323ebb1c6082821a54962b839832b03984598375bff3975b804423" +dependencies = [ + "digest 0.10.7", + "sha1", ] [[package]] @@ -3650,8 +4935,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", ] [[package]] @@ -3662,6 +4958,12 @@ checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] + +[[package]] +name = "shell-words" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77" [[package]] name = "shlex" @@ -3690,6 +4992,16 @@ "tracing-subscriber", ] [[package]] +name = "signal-hook" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a0c28ca5908dbdbcd52e6fdaa00358ab88637f8ab33e1f188dd510eb44b53d" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] name = "signal-hook-registry" version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3705,7 +5017,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest", + "digest 0.10.7", "rand_core 0.6.4", ] @@ -3848,7 +5160,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags", + "bitflags 2.11.1", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -4216,6 +5528,7 @@ dependencies = [ "futures-core", "pin-project-lite", "tokio", + "tokio-util", ] [[package]] @@ -4336,6 +5649,74 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] +name = "tonic" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" +dependencies = [ + "async-trait", + "axum", + "base64", + "bytes", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "socket2", + "sync_wrapper", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f61875ac5293cf72e6c8cf0158086428c82c37229e98c840878f1706b0322" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tonic-prost" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0" +dependencies = [ + "bytes", + "prost", + "tonic", +] + +[[package]] +name = "tonic-prost-build" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "654e5643eff75d7f8c99197ce1440ed19a3474eada74c12bbac488b2cafdae27" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn", + "tempfile", + "tonic-build", +] + +[[package]] name = "tower" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4343,9 +5724,12 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", + "indexmap", "pin-project-lite", + "slab", "sync_wrapper", "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -4358,7 +5742,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ "async-compression", - "bitflags", + "bitflags 2.11.1", "bytes", "futures-core", "futures-util", @@ -4560,12 +5944,42 @@ "syn", ] [[package]] +name = "uluru" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c8a2469e56e6e5095c82ccd3afb98dad95f7af7929aab6d8ba8d6e0f73657da" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[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.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + +[[package]] name = "unicode-segmentation" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4805,7 +6219,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags", + "bitflags 2.11.1", "hashbrown 0.15.5", "indexmap", "semver", @@ -5214,7 +6628,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags", + "bitflags 2.11.1", "indexmap", "log", "serde", @@ -5358,6 +6772,12 @@ "proc-macro2", "quote", "syn", ] + +[[package]] +name = "zlib-rs" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "977347db8caa080403f6b6b7c1cda9479a8e869316f7e13a59b19076a40f94e3" [[package]] name = "zmij" diff --git a/Cargo.toml b/Cargo.toml --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["bobbin/crates/*", "shuttle"] +members = ["bobbin/crates/*", "shuttle", "gitmirror"] exclude = ["sites"] [workspace.package] @@ -84,6 +84,11 @@ tantivy = "0.26" tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt", "json"] } + +tonic = "0.14" +tonic-prost = "0.14" +tonic-prost-build = "0.14" +prost = "0.14" thiserror = "2" diff --git a/docker-compose.yml b/docker-compose.yml --- a/docker-compose.yml +++ b/docker-compose.yml @@ -284,6 +284,20 @@ knotmirror-tap: condition: service_started networks: [tngl] + gitmirror: + build: + context: . + dockerfile: localinfra/gitmirror.Dockerfile + restart: unless-stopped + environment: + RUST_LOG: gitmirror=debug + GITMIRROR_ADDR: 0.0.0.0:9000 + GITMIRROR_REPO_BASE: /data/repos + volumes: + - knotmirror-data:/data + - ./localinfra/certs/root.crt:/usr/local/share/ca-certificates/caddy.crt:ro + networks: [tngl] + zoekt-webserver: build: context: https://tangled.org/boltless.me/zoekt.git#tngl @@ -392,6 +406,7 @@ - knot.tngl.boltless.dev - spindle.tngl.boltless.dev - tngl.boltless.dev - mirror.tngl.boltless.dev + - gitmirror.tngl.boltless.dev - zoekt.tngl.boltless.dev - pdsls.tngl.boltless.dev diff --git a/flake.nix b/flake.nix --- a/flake.nix +++ b/flake.nix @@ -357,6 +357,7 @@ pkgs.protoc-gen-prost pkgs.protoc-gen-prost-crate pkgs.protoc-gen-prost-serde pkgs.protoc-gen-go + pkgs.protoc-gen-go-grpc (fenix.packages.${system}.combine [ fenix.packages.${system}.stable.cargo fenix.packages.${system}.stable.rustc @@ -463,6 +464,8 @@ rootDir=$(git rev-parse --show-toplevel 2>/dev/null || pwd) cd "$rootDir" echo ">>> regenerating protobuf files.." buf generate + echo ">>> regenerating gitmirror go stubs..." + buf generate gitmirror --template gitmirror/buf.gen.yaml echo ">>> generating file descriptor set for shuttle..." buf build -o shuttle/src/gen/file_descriptor_set.bin echo ">>> done" diff --git a/gitmirror/Cargo.toml b/gitmirror/Cargo.toml new file mode 100644 --- /dev/null +++ b/gitmirror/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "gitmirror" +version = "0.1.0" +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[dependencies] +anyhow = { workspace = true } +clap = { workspace = true } +tonic = { workspace = true } +tonic-prost = { workspace = true } +prost = { workspace = true } +tokio = { workspace = true, features = ["net", "rt-multi-thread", "sync"] } +tokio-stream = { workspace = true, features = ["sync"] } +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +line-numbers = "0.4.0" +rustc-hash = "2.1.2" +gix = { version = "0.84", features = ["parallel", "blob-diff", "merge", "sha1", "sha256", "revision", "tree-editor"] } +prost-types = "0.14.4" + +[build-dependencies] +tonic-prost-build = { workspace = true } + +[dev-dependencies] +tempfile = "3" + +[lints] +workspace = true diff --git a/gitmirror/README.md b/gitmirror/README.md new file mode 100644 --- /dev/null +++ b/gitmirror/README.md @@ -0,0 +1,4 @@ +# Gitmirror + +gitmirror is alternative name of Knotmirror v2. It is designed to be run under the AppView as git syncing service. +Because it's internal sync service like tap or hydrant, it uses grpc over xrpc. diff --git a/gitmirror/buf.gen.yaml b/gitmirror/buf.gen.yaml new file mode 100644 --- /dev/null +++ b/gitmirror/buf.gen.yaml @@ -0,0 +1,12 @@ +version: v2 +plugins: + - local: protoc-gen-go + out: . + opt: + - paths=import + - module=tangled.org/core + - local: protoc-gen-go-grpc + out: . + opt: + - paths=import + - module=tangled.org/core diff --git a/gitmirror/buf.yaml b/gitmirror/buf.yaml new file mode 100644 --- /dev/null +++ b/gitmirror/buf.yaml @@ -0,0 +1,3 @@ +version: v2 +modules: + - path: proto diff --git a/gitmirror/build.rs b/gitmirror/build.rs new file mode 100644 --- /dev/null +++ b/gitmirror/build.rs @@ -0,0 +1,4 @@ +fn main() -> Result<(), Box> { + tonic_prost_build::compile_protos("proto/gitmirror/v1/gitmirror.proto")?; + Ok(()) +} diff --git a/gitmirror/example/client.go b/gitmirror/example/client.go new file mode 100644 --- /dev/null +++ b/gitmirror/example/client.go @@ -0,0 +1,42 @@ +//go:build ignore + +// Example: how appview will call gitmirror's GitMirrorService. +// +// This file is build-ignored so it never breaks `go build ./...`. It is a +// copy-ready reference for wiring the client into appview once the Go stubs +// are generated (see gitmirror/buf.gen.yaml). +package main + +import ( + "context" + "log" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + gitmirrorv1 "tangled.org/core/gitmirror/proto/gen" +) + +func main() { + conn, err := grpc.NewClient("127.0.0.1:9000", + grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Fatal(err) + } + defer conn.Close() + + client := gitmirrorv1.NewGitMirrorServiceClient(conn) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // appview converts its syntax.DID to a string via did.String(). + resp, err := client.Commits(ctx, &gitmirrorv1.CommitsRequest{ + Repo: "did:plc:example", + Options: &gitmirrorv1.CommitsOptions{}, + }) + if err != nil { + log.Fatal(err) + } + log.Println(resp.GetCommits()) +} diff --git a/gitmirror/proto/gen/gitmirror.pb.go b/gitmirror/proto/gen/gitmirror.pb.go new file mode 100644 --- /dev/null +++ b/gitmirror/proto/gen/gitmirror.pb.go @@ -0,0 +1,1119 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: gitmirror/v1/gitmirror.proto + +package gitmirrorv1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type DiffRequest_ComparisonType int32 + +const ( + DiffRequest_COMPARISON_TYPE_UNSPECIFIED DiffRequest_ComparisonType = 0 + // Corresponds to the BASE..HEAD syntax that only returns any commits that + // are in HEAD but not in BASE. + DiffRequest_COMPARISON_TYPE_ONLY_IN_HEAD DiffRequest_ComparisonType = 1 + // Corresponds to the BASE...HEAD syntax that returns any commits that are + // not in both BASE and HEAD. + DiffRequest_COMPARISON_TYPE_INTERSECTION DiffRequest_ComparisonType = 2 +) + +// Enum value maps for DiffRequest_ComparisonType. +var ( + DiffRequest_ComparisonType_name = map[int32]string{ + 0: "COMPARISON_TYPE_UNSPECIFIED", + 1: "COMPARISON_TYPE_ONLY_IN_HEAD", + 2: "COMPARISON_TYPE_INTERSECTION", + } + DiffRequest_ComparisonType_value = map[string]int32{ + "COMPARISON_TYPE_UNSPECIFIED": 0, + "COMPARISON_TYPE_ONLY_IN_HEAD": 1, + "COMPARISON_TYPE_INTERSECTION": 2, + } +) + +func (x DiffRequest_ComparisonType) Enum() *DiffRequest_ComparisonType { + p := new(DiffRequest_ComparisonType) + *p = x + return p +} + +func (x DiffRequest_ComparisonType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (DiffRequest_ComparisonType) Descriptor() protoreflect.EnumDescriptor { + return file_gitmirror_v1_gitmirror_proto_enumTypes[0].Descriptor() +} + +func (DiffRequest_ComparisonType) Type() protoreflect.EnumType { + return &file_gitmirror_v1_gitmirror_proto_enumTypes[0] +} + +func (x DiffRequest_ComparisonType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use DiffRequest_ComparisonType.Descriptor instead. +func (DiffRequest_ComparisonType) EnumDescriptor() ([]byte, []int) { + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{4, 0} +} + +type CommitLogRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // repo DID + Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` + // Ranges to include in the git log (revspec, "A..B", "A...B", etc.). + // At least one range, or all_refs must be specified. + Ranges [][]byte `protobuf:"bytes,2,rep,name=ranges,proto3" json:"ranges,omitempty"` + // If true, all refs are searched for commits. + // Must not be true when ranges are given. + AllRefs bool `protobuf:"varint,3,opt,name=all_refs,json=allRefs,proto3" json:"all_refs,omitempty"` + // After is an optional parameter to specify the earliest commit to consider. + After *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=after,proto3" json:"after,omitempty"` + // Before is an optional parameter to specify the latest commit to consider. + Before *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=before,proto3" json:"before,omitempty"` + // MaxCommits is an optional parameter to specify the maximum number of commits + // to return. If max_commits is 0, all commits that match the criteria will be + // returned. + MaxCommits uint32 `protobuf:"varint,6,opt,name=max_commits,json=maxCommits,proto3" json:"max_commits,omitempty"` + // Skip is an optional parameter to specify the number of commits to skip. + // This can be used to implement a poor mans pagination. + Skip uint32 `protobuf:"varint,7,opt,name=skip,proto3" json:"skip,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CommitLogRequest) Reset() { + *x = CommitLogRequest{} + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CommitLogRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CommitLogRequest) ProtoMessage() {} + +func (x *CommitLogRequest) ProtoReflect() protoreflect.Message { + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CommitLogRequest.ProtoReflect.Descriptor instead. +func (*CommitLogRequest) Descriptor() ([]byte, []int) { + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{0} +} + +func (x *CommitLogRequest) GetRepo() string { + if x != nil { + return x.Repo + } + return "" +} + +func (x *CommitLogRequest) GetRanges() [][]byte { + if x != nil { + return x.Ranges + } + return nil +} + +func (x *CommitLogRequest) GetAllRefs() bool { + if x != nil { + return x.AllRefs + } + return false +} + +func (x *CommitLogRequest) GetAfter() *timestamppb.Timestamp { + if x != nil { + return x.After + } + return nil +} + +func (x *CommitLogRequest) GetBefore() *timestamppb.Timestamp { + if x != nil { + return x.Before + } + return nil +} + +func (x *CommitLogRequest) GetMaxCommits() uint32 { + if x != nil { + return x.MaxCommits + } + return 0 +} + +func (x *CommitLogRequest) GetSkip() uint32 { + if x != nil { + return x.Skip + } + return 0 +} + +type CommitLogResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Commits []*GitCommit `protobuf:"bytes,1,rep,name=commits,proto3" json:"commits,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CommitLogResponse) Reset() { + *x = CommitLogResponse{} + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CommitLogResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CommitLogResponse) ProtoMessage() {} + +func (x *CommitLogResponse) ProtoReflect() protoreflect.Message { + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CommitLogResponse.ProtoReflect.Descriptor instead. +func (*CommitLogResponse) Descriptor() ([]byte, []int) { + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{1} +} + +func (x *CommitLogResponse) GetCommits() []*GitCommit { + if x != nil { + return x.Commits + } + return nil +} + +type GetBlobRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // repo carries the DID as a string. + Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` + // oid is the blob object id (hex). + Oid string `protobuf:"bytes,2,opt,name=oid,proto3" json:"oid,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetBlobRequest) Reset() { + *x = GetBlobRequest{} + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetBlobRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBlobRequest) ProtoMessage() {} + +func (x *GetBlobRequest) ProtoReflect() protoreflect.Message { + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetBlobRequest.ProtoReflect.Descriptor instead. +func (*GetBlobRequest) Descriptor() ([]byte, []int) { + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{2} +} + +func (x *GetBlobRequest) GetRepo() string { + if x != nil { + return x.Repo + } + return "" +} + +func (x *GetBlobRequest) GetOid() string { + if x != nil { + return x.Oid + } + return "" +} + +type BlobChunk struct { + state protoimpl.MessageState `protogen:"open.v1"` + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BlobChunk) Reset() { + *x = BlobChunk{} + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BlobChunk) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BlobChunk) ProtoMessage() {} + +func (x *BlobChunk) ProtoReflect() protoreflect.Message { + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BlobChunk.ProtoReflect.Descriptor instead. +func (*BlobChunk) Descriptor() ([]byte, []int) { + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{3} +} + +func (x *BlobChunk) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +type DiffRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // repo carries the DID as a string; parsing/validation is an impl detail. + Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` + // base_rev_spec/head_rev_spec are commit-ish ref strings. + // base_rev_spec is optional; when unset, diff head against head's first parent. + BaseRevSpec []byte `protobuf:"bytes,2,opt,name=base_rev_spec,json=baseRevSpec,proto3,oneof" json:"base_rev_spec,omitempty"` + HeadRevSpec []byte `protobuf:"bytes,3,opt,name=head_rev_spec,json=headRevSpec,proto3" json:"head_rev_spec,omitempty"` + ComparisonType DiffRequest_ComparisonType `protobuf:"varint,4,opt,name=comparison_type,json=comparisonType,proto3,enum=gitmirror.v1.DiffRequest_ComparisonType" json:"comparison_type,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DiffRequest) Reset() { + *x = DiffRequest{} + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DiffRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DiffRequest) ProtoMessage() {} + +func (x *DiffRequest) ProtoReflect() protoreflect.Message { + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DiffRequest.ProtoReflect.Descriptor instead. +func (*DiffRequest) Descriptor() ([]byte, []int) { + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{4} +} + +func (x *DiffRequest) GetRepo() string { + if x != nil { + return x.Repo + } + return "" +} + +func (x *DiffRequest) GetBaseRevSpec() []byte { + if x != nil { + return x.BaseRevSpec + } + return nil +} + +func (x *DiffRequest) GetHeadRevSpec() []byte { + if x != nil { + return x.HeadRevSpec + } + return nil +} + +func (x *DiffRequest) GetComparisonType() DiffRequest_ComparisonType { + if x != nil { + return x.ComparisonType + } + return DiffRequest_COMPARISON_TYPE_UNSPECIFIED +} + +type InterdiffRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // repo carries the DID as a string. + Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` + // Old patch range (from_base..from_head) and new patch range (to_base..to_head). + FromBase []byte `protobuf:"bytes,2,opt,name=from_base,json=fromBase,proto3" json:"from_base,omitempty"` + FromHead []byte `protobuf:"bytes,3,opt,name=from_head,json=fromHead,proto3" json:"from_head,omitempty"` + ToBase []byte `protobuf:"bytes,4,opt,name=to_base,json=toBase,proto3" json:"to_base,omitempty"` + ToHead []byte `protobuf:"bytes,5,opt,name=to_head,json=toHead,proto3" json:"to_head,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InterdiffRequest) Reset() { + *x = InterdiffRequest{} + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InterdiffRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InterdiffRequest) ProtoMessage() {} + +func (x *InterdiffRequest) ProtoReflect() protoreflect.Message { + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InterdiffRequest.ProtoReflect.Descriptor instead. +func (*InterdiffRequest) Descriptor() ([]byte, []int) { + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{5} +} + +func (x *InterdiffRequest) GetRepo() string { + if x != nil { + return x.Repo + } + return "" +} + +func (x *InterdiffRequest) GetFromBase() []byte { + if x != nil { + return x.FromBase + } + return nil +} + +func (x *InterdiffRequest) GetFromHead() []byte { + if x != nil { + return x.FromHead + } + return nil +} + +func (x *InterdiffRequest) GetToBase() []byte { + if x != nil { + return x.ToBase + } + return nil +} + +func (x *InterdiffRequest) GetToHead() []byte { + if x != nil { + return x.ToHead + } + return nil +} + +type FileContent struct { + state protoimpl.MessageState `protogen:"open.v1"` + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Oid string `protobuf:"bytes,2,opt,name=oid,proto3" json:"oid,omitempty"` + Size uint64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"` + IsBinary bool `protobuf:"varint,4,opt,name=is_binary,json=isBinary,proto3" json:"is_binary,omitempty"` + IsSubmodule bool `protobuf:"varint,5,opt,name=is_submodule,json=isSubmodule,proto3" json:"is_submodule,omitempty"` + // Raw file bytes, inlined when the oid is not fetchable by the client + // (e.g. interdiff) + Content []byte `protobuf:"bytes,6,opt,name=content,proto3,oneof" json:"content,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FileContent) Reset() { + *x = FileContent{} + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FileContent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FileContent) ProtoMessage() {} + +func (x *FileContent) ProtoReflect() protoreflect.Message { + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FileContent.ProtoReflect.Descriptor instead. +func (*FileContent) Descriptor() ([]byte, []int) { + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{6} +} + +func (x *FileContent) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *FileContent) GetOid() string { + if x != nil { + return x.Oid + } + return "" +} + +func (x *FileContent) GetSize() uint64 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *FileContent) GetIsBinary() bool { + if x != nil { + return x.IsBinary + } + return false +} + +func (x *FileContent) GetIsSubmodule() bool { + if x != nil { + return x.IsSubmodule + } + return false +} + +func (x *FileContent) GetContent() []byte { + if x != nil { + return x.Content + } + return nil +} + +// One aligned line pair; a side is absent (None) when unset. +type LinePair struct { + state protoimpl.MessageState `protogen:"open.v1"` + Lhs *uint32 `protobuf:"varint,1,opt,name=lhs,proto3,oneof" json:"lhs,omitempty"` + Rhs *uint32 `protobuf:"varint,2,opt,name=rhs,proto3,oneof" json:"rhs,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LinePair) Reset() { + *x = LinePair{} + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LinePair) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LinePair) ProtoMessage() {} + +func (x *LinePair) ProtoReflect() protoreflect.Message { + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LinePair.ProtoReflect.Descriptor instead. +func (*LinePair) Descriptor() ([]byte, []int) { + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{7} +} + +func (x *LinePair) GetLhs() uint32 { + if x != nil && x.Lhs != nil { + return *x.Lhs + } + return 0 +} + +func (x *LinePair) GetRhs() uint32 { + if x != nil && x.Rhs != nil { + return *x.Rhs + } + return 0 +} + +type Hunk struct { + state protoimpl.MessageState `protogen:"open.v1"` + // LineNumber (u32) sets containing novel content per side. + NovelLhs []uint32 `protobuf:"varint,1,rep,packed,name=novel_lhs,json=novelLhs,proto3" json:"novel_lhs,omitempty"` + NovelRhs []uint32 `protobuf:"varint,2,rep,packed,name=novel_rhs,json=novelRhs,proto3" json:"novel_rhs,omitempty"` + Lines []*LinePair `protobuf:"bytes,3,rep,name=lines,proto3" json:"lines,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Hunk) Reset() { + *x = Hunk{} + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Hunk) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Hunk) ProtoMessage() {} + +func (x *Hunk) ProtoReflect() protoreflect.Message { + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Hunk.ProtoReflect.Descriptor instead. +func (*Hunk) Descriptor() ([]byte, []int) { + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{8} +} + +func (x *Hunk) GetNovelLhs() []uint32 { + if x != nil { + return x.NovelLhs + } + return nil +} + +func (x *Hunk) GetNovelRhs() []uint32 { + if x != nil { + return x.NovelRhs + } + return nil +} + +func (x *Hunk) GetLines() []*LinePair { + if x != nil { + return x.Lines + } + return nil +} + +// Number of bytes per side when the files differ (Rust Option<(usize, usize)>). +type ByteChanges struct { + state protoimpl.MessageState `protogen:"open.v1"` + Lhs uint64 `protobuf:"varint,1,opt,name=lhs,proto3" json:"lhs,omitempty"` + Rhs uint64 `protobuf:"varint,2,opt,name=rhs,proto3" json:"rhs,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ByteChanges) Reset() { + *x = ByteChanges{} + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ByteChanges) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ByteChanges) ProtoMessage() {} + +func (x *ByteChanges) ProtoReflect() protoreflect.Message { + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ByteChanges.ProtoReflect.Descriptor instead. +func (*ByteChanges) Descriptor() ([]byte, []int) { + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{9} +} + +func (x *ByteChanges) GetLhs() uint64 { + if x != nil { + return x.Lhs + } + return 0 +} + +func (x *ByteChanges) GetRhs() uint64 { + if x != nil { + return x.Rhs + } + return 0 +} + +type FileDiff struct { + state protoimpl.MessageState `protogen:"open.v1"` + LhsSrc *FileContent `protobuf:"bytes,1,opt,name=lhs_src,json=lhsSrc,proto3" json:"lhs_src,omitempty"` + RhsSrc *FileContent `protobuf:"bytes,2,opt,name=rhs_src,json=rhsSrc,proto3" json:"rhs_src,omitempty"` + Hunks []*Hunk `protobuf:"bytes,3,rep,name=hunks,proto3" json:"hunks,omitempty"` + HasByteChanges *ByteChanges `protobuf:"bytes,4,opt,name=has_byte_changes,json=hasByteChanges,proto3,oneof" json:"has_byte_changes,omitempty"` + HasSyntacticChanges bool `protobuf:"varint,5,opt,name=has_syntactic_changes,json=hasSyntacticChanges,proto3" json:"has_syntactic_changes,omitempty"` // TODO: lhs_positions & rhs_positions + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FileDiff) Reset() { + *x = FileDiff{} + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FileDiff) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FileDiff) ProtoMessage() {} + +func (x *FileDiff) ProtoReflect() protoreflect.Message { + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FileDiff.ProtoReflect.Descriptor instead. +func (*FileDiff) Descriptor() ([]byte, []int) { + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{10} +} + +func (x *FileDiff) GetLhsSrc() *FileContent { + if x != nil { + return x.LhsSrc + } + return nil +} + +func (x *FileDiff) GetRhsSrc() *FileContent { + if x != nil { + return x.RhsSrc + } + return nil +} + +func (x *FileDiff) GetHunks() []*Hunk { + if x != nil { + return x.Hunks + } + return nil +} + +func (x *FileDiff) GetHasByteChanges() *ByteChanges { + if x != nil { + return x.HasByteChanges + } + return nil +} + +func (x *FileDiff) GetHasSyntacticChanges() bool { + if x != nil { + return x.HasSyntacticChanges + } + return false +} + +type GitCommit struct { + state protoimpl.MessageState `protogen:"open.v1"` + Oid string `protobuf:"bytes,1,opt,name=oid,proto3" json:"oid,omitempty"` + Author *GitSignature `protobuf:"bytes,2,opt,name=author,proto3" json:"author,omitempty"` + Committer *GitSignature `protobuf:"bytes,3,opt,name=committer,proto3" json:"committer,omitempty"` + Message []byte `protobuf:"bytes,4,opt,name=message,proto3" json:"message,omitempty"` + Parents []string `protobuf:"bytes,5,rep,name=parents,proto3" json:"parents,omitempty"` + ExtraHeaders map[string]string `protobuf:"bytes,6,rep,name=extra_headers,json=extraHeaders,proto3" json:"extra_headers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GitCommit) Reset() { + *x = GitCommit{} + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GitCommit) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GitCommit) ProtoMessage() {} + +func (x *GitCommit) ProtoReflect() protoreflect.Message { + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GitCommit.ProtoReflect.Descriptor instead. +func (*GitCommit) Descriptor() ([]byte, []int) { + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{11} +} + +func (x *GitCommit) GetOid() string { + if x != nil { + return x.Oid + } + return "" +} + +func (x *GitCommit) GetAuthor() *GitSignature { + if x != nil { + return x.Author + } + return nil +} + +func (x *GitCommit) GetCommitter() *GitSignature { + if x != nil { + return x.Committer + } + return nil +} + +func (x *GitCommit) GetMessage() []byte { + if x != nil { + return x.Message + } + return nil +} + +func (x *GitCommit) GetParents() []string { + if x != nil { + return x.Parents + } + return nil +} + +func (x *GitCommit) GetExtraHeaders() map[string]string { + if x != nil { + return x.ExtraHeaders + } + return nil +} + +type GitSignature struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Email []byte `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"` + Date *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=date,proto3" json:"date,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GitSignature) Reset() { + *x = GitSignature{} + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GitSignature) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GitSignature) ProtoMessage() {} + +func (x *GitSignature) ProtoReflect() protoreflect.Message { + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GitSignature.ProtoReflect.Descriptor instead. +func (*GitSignature) Descriptor() ([]byte, []int) { + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{12} +} + +func (x *GitSignature) GetName() []byte { + if x != nil { + return x.Name + } + return nil +} + +func (x *GitSignature) GetEmail() []byte { + if x != nil { + return x.Email + } + return nil +} + +func (x *GitSignature) GetDate() *timestamppb.Timestamp { + if x != nil { + return x.Date + } + return nil +} + +var File_gitmirror_v1_gitmirror_proto protoreflect.FileDescriptor + +const file_gitmirror_v1_gitmirror_proto_rawDesc = "" + + "\n" + + "\x1cgitmirror/v1/gitmirror.proto\x12\fgitmirror.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"\xf4\x01\n" + + "\x10CommitLogRequest\x12\x12\n" + + "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" + + "\x06ranges\x18\x02 \x03(\fR\x06ranges\x12\x19\n" + + "\ball_refs\x18\x03 \x01(\bR\aallRefs\x120\n" + + "\x05after\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\x05after\x122\n" + + "\x06before\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\x06before\x12\x1f\n" + + "\vmax_commits\x18\x06 \x01(\rR\n" + + "maxCommits\x12\x12\n" + + "\x04skip\x18\a \x01(\rR\x04skip\"F\n" + + "\x11CommitLogResponse\x121\n" + + "\acommits\x18\x01 \x03(\v2\x17.gitmirror.v1.GitCommitR\acommits\"6\n" + + "\x0eGetBlobRequest\x12\x12\n" + + "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x10\n" + + "\x03oid\x18\x02 \x01(\tR\x03oid\"\x1f\n" + + "\tBlobChunk\x12\x12\n" + + "\x04data\x18\x01 \x01(\fR\x04data\"\xca\x02\n" + + "\vDiffRequest\x12\x12\n" + + "\x04repo\x18\x01 \x01(\tR\x04repo\x12'\n" + + "\rbase_rev_spec\x18\x02 \x01(\fH\x00R\vbaseRevSpec\x88\x01\x01\x12\"\n" + + "\rhead_rev_spec\x18\x03 \x01(\fR\vheadRevSpec\x12Q\n" + + "\x0fcomparison_type\x18\x04 \x01(\x0e2(.gitmirror.v1.DiffRequest.ComparisonTypeR\x0ecomparisonType\"u\n" + + "\x0eComparisonType\x12\x1f\n" + + "\x1bCOMPARISON_TYPE_UNSPECIFIED\x10\x00\x12 \n" + + "\x1cCOMPARISON_TYPE_ONLY_IN_HEAD\x10\x01\x12 \n" + + "\x1cCOMPARISON_TYPE_INTERSECTION\x10\x02B\x10\n" + + "\x0e_base_rev_spec\"\x92\x01\n" + + "\x10InterdiffRequest\x12\x12\n" + + "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x1b\n" + + "\tfrom_base\x18\x02 \x01(\fR\bfromBase\x12\x1b\n" + + "\tfrom_head\x18\x03 \x01(\fR\bfromHead\x12\x17\n" + + "\ato_base\x18\x04 \x01(\fR\x06toBase\x12\x17\n" + + "\ato_head\x18\x05 \x01(\fR\x06toHead\"\xb2\x01\n" + + "\vFileContent\x12\x12\n" + + "\x04path\x18\x01 \x01(\tR\x04path\x12\x10\n" + + "\x03oid\x18\x02 \x01(\tR\x03oid\x12\x12\n" + + "\x04size\x18\x03 \x01(\x04R\x04size\x12\x1b\n" + + "\tis_binary\x18\x04 \x01(\bR\bisBinary\x12!\n" + + "\fis_submodule\x18\x05 \x01(\bR\visSubmodule\x12\x1d\n" + + "\acontent\x18\x06 \x01(\fH\x00R\acontent\x88\x01\x01B\n" + + "\n" + + "\b_content\"H\n" + + "\bLinePair\x12\x15\n" + + "\x03lhs\x18\x01 \x01(\rH\x00R\x03lhs\x88\x01\x01\x12\x15\n" + + "\x03rhs\x18\x02 \x01(\rH\x01R\x03rhs\x88\x01\x01B\x06\n" + + "\x04_lhsB\x06\n" + + "\x04_rhs\"n\n" + + "\x04Hunk\x12\x1b\n" + + "\tnovel_lhs\x18\x01 \x03(\rR\bnovelLhs\x12\x1b\n" + + "\tnovel_rhs\x18\x02 \x03(\rR\bnovelRhs\x12,\n" + + "\x05lines\x18\x03 \x03(\v2\x16.gitmirror.v1.LinePairR\x05lines\"1\n" + + "\vByteChanges\x12\x10\n" + + "\x03lhs\x18\x01 \x01(\x04R\x03lhs\x12\x10\n" + + "\x03rhs\x18\x02 \x01(\x04R\x03rhs\"\xaf\x02\n" + + "\bFileDiff\x122\n" + + "\alhs_src\x18\x01 \x01(\v2\x19.gitmirror.v1.FileContentR\x06lhsSrc\x122\n" + + "\arhs_src\x18\x02 \x01(\v2\x19.gitmirror.v1.FileContentR\x06rhsSrc\x12(\n" + + "\x05hunks\x18\x03 \x03(\v2\x12.gitmirror.v1.HunkR\x05hunks\x12H\n" + + "\x10has_byte_changes\x18\x04 \x01(\v2\x19.gitmirror.v1.ByteChangesH\x00R\x0ehasByteChanges\x88\x01\x01\x122\n" + + "\x15has_syntactic_changes\x18\x05 \x01(\bR\x13hasSyntacticChangesB\x13\n" + + "\x11_has_byte_changes\"\xd0\x02\n" + + "\tGitCommit\x12\x10\n" + + "\x03oid\x18\x01 \x01(\tR\x03oid\x122\n" + + "\x06author\x18\x02 \x01(\v2\x1a.gitmirror.v1.GitSignatureR\x06author\x128\n" + + "\tcommitter\x18\x03 \x01(\v2\x1a.gitmirror.v1.GitSignatureR\tcommitter\x12\x18\n" + + "\amessage\x18\x04 \x01(\fR\amessage\x12\x18\n" + + "\aparents\x18\x05 \x03(\tR\aparents\x12N\n" + + "\rextra_headers\x18\x06 \x03(\v2).gitmirror.v1.GitCommit.ExtraHeadersEntryR\fextraHeaders\x1a?\n" + + "\x11ExtraHeadersEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"h\n" + + "\fGitSignature\x12\x12\n" + + "\x04name\x18\x01 \x01(\fR\x04name\x12\x14\n" + + "\x05email\x18\x02 \x01(\fR\x05email\x12.\n" + + "\x04date\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\x04date2\xaa\x02\n" + + "\x10GitMirrorService\x12N\n" + + "\tCommitLog\x12\x1e.gitmirror.v1.CommitLogRequest\x1a\x1f.gitmirror.v1.CommitLogResponse0\x01\x12B\n" + + "\aGetBlob\x12\x1c.gitmirror.v1.GetBlobRequest\x1a\x17.gitmirror.v1.BlobChunk0\x01\x12;\n" + + "\x04Diff\x12\x19.gitmirror.v1.DiffRequest\x1a\x16.gitmirror.v1.FileDiff0\x01\x12E\n" + + "\tInterdiff\x12\x1e.gitmirror.v1.InterdiffRequest\x1a\x16.gitmirror.v1.FileDiff0\x01B2Z0tangled.org/core/gitmirror/proto/gen;gitmirrorv1b\x06proto3" + +var ( + file_gitmirror_v1_gitmirror_proto_rawDescOnce sync.Once + file_gitmirror_v1_gitmirror_proto_rawDescData []byte +) + +func file_gitmirror_v1_gitmirror_proto_rawDescGZIP() []byte { + file_gitmirror_v1_gitmirror_proto_rawDescOnce.Do(func() { + file_gitmirror_v1_gitmirror_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_gitmirror_v1_gitmirror_proto_rawDesc), len(file_gitmirror_v1_gitmirror_proto_rawDesc))) + }) + return file_gitmirror_v1_gitmirror_proto_rawDescData +} + +var file_gitmirror_v1_gitmirror_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_gitmirror_v1_gitmirror_proto_msgTypes = make([]protoimpl.MessageInfo, 14) +var file_gitmirror_v1_gitmirror_proto_goTypes = []any{ + (DiffRequest_ComparisonType)(0), // 0: gitmirror.v1.DiffRequest.ComparisonType + (*CommitLogRequest)(nil), // 1: gitmirror.v1.CommitLogRequest + (*CommitLogResponse)(nil), // 2: gitmirror.v1.CommitLogResponse + (*GetBlobRequest)(nil), // 3: gitmirror.v1.GetBlobRequest + (*BlobChunk)(nil), // 4: gitmirror.v1.BlobChunk + (*DiffRequest)(nil), // 5: gitmirror.v1.DiffRequest + (*InterdiffRequest)(nil), // 6: gitmirror.v1.InterdiffRequest + (*FileContent)(nil), // 7: gitmirror.v1.FileContent + (*LinePair)(nil), // 8: gitmirror.v1.LinePair + (*Hunk)(nil), // 9: gitmirror.v1.Hunk + (*ByteChanges)(nil), // 10: gitmirror.v1.ByteChanges + (*FileDiff)(nil), // 11: gitmirror.v1.FileDiff + (*GitCommit)(nil), // 12: gitmirror.v1.GitCommit + (*GitSignature)(nil), // 13: gitmirror.v1.GitSignature + nil, // 14: gitmirror.v1.GitCommit.ExtraHeadersEntry + (*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp +} +var file_gitmirror_v1_gitmirror_proto_depIdxs = []int32{ + 15, // 0: gitmirror.v1.CommitLogRequest.after:type_name -> google.protobuf.Timestamp + 15, // 1: gitmirror.v1.CommitLogRequest.before:type_name -> google.protobuf.Timestamp + 12, // 2: gitmirror.v1.CommitLogResponse.commits:type_name -> gitmirror.v1.GitCommit + 0, // 3: gitmirror.v1.DiffRequest.comparison_type:type_name -> gitmirror.v1.DiffRequest.ComparisonType + 8, // 4: gitmirror.v1.Hunk.lines:type_name -> gitmirror.v1.LinePair + 7, // 5: gitmirror.v1.FileDiff.lhs_src:type_name -> gitmirror.v1.FileContent + 7, // 6: gitmirror.v1.FileDiff.rhs_src:type_name -> gitmirror.v1.FileContent + 9, // 7: gitmirror.v1.FileDiff.hunks:type_name -> gitmirror.v1.Hunk + 10, // 8: gitmirror.v1.FileDiff.has_byte_changes:type_name -> gitmirror.v1.ByteChanges + 13, // 9: gitmirror.v1.GitCommit.author:type_name -> gitmirror.v1.GitSignature + 13, // 10: gitmirror.v1.GitCommit.committer:type_name -> gitmirror.v1.GitSignature + 14, // 11: gitmirror.v1.GitCommit.extra_headers:type_name -> gitmirror.v1.GitCommit.ExtraHeadersEntry + 15, // 12: gitmirror.v1.GitSignature.date:type_name -> google.protobuf.Timestamp + 1, // 13: gitmirror.v1.GitMirrorService.CommitLog:input_type -> gitmirror.v1.CommitLogRequest + 3, // 14: gitmirror.v1.GitMirrorService.GetBlob:input_type -> gitmirror.v1.GetBlobRequest + 5, // 15: gitmirror.v1.GitMirrorService.Diff:input_type -> gitmirror.v1.DiffRequest + 6, // 16: gitmirror.v1.GitMirrorService.Interdiff:input_type -> gitmirror.v1.InterdiffRequest + 2, // 17: gitmirror.v1.GitMirrorService.CommitLog:output_type -> gitmirror.v1.CommitLogResponse + 4, // 18: gitmirror.v1.GitMirrorService.GetBlob:output_type -> gitmirror.v1.BlobChunk + 11, // 19: gitmirror.v1.GitMirrorService.Diff:output_type -> gitmirror.v1.FileDiff + 11, // 20: gitmirror.v1.GitMirrorService.Interdiff:output_type -> gitmirror.v1.FileDiff + 17, // [17:21] is the sub-list for method output_type + 13, // [13:17] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name +} + +func init() { file_gitmirror_v1_gitmirror_proto_init() } +func file_gitmirror_v1_gitmirror_proto_init() { + if File_gitmirror_v1_gitmirror_proto != nil { + return + } + file_gitmirror_v1_gitmirror_proto_msgTypes[4].OneofWrappers = []any{} + file_gitmirror_v1_gitmirror_proto_msgTypes[6].OneofWrappers = []any{} + file_gitmirror_v1_gitmirror_proto_msgTypes[7].OneofWrappers = []any{} + file_gitmirror_v1_gitmirror_proto_msgTypes[10].OneofWrappers = []any{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_gitmirror_v1_gitmirror_proto_rawDesc), len(file_gitmirror_v1_gitmirror_proto_rawDesc)), + NumEnums: 1, + NumMessages: 14, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_gitmirror_v1_gitmirror_proto_goTypes, + DependencyIndexes: file_gitmirror_v1_gitmirror_proto_depIdxs, + EnumInfos: file_gitmirror_v1_gitmirror_proto_enumTypes, + MessageInfos: file_gitmirror_v1_gitmirror_proto_msgTypes, + }.Build() + File_gitmirror_v1_gitmirror_proto = out.File + file_gitmirror_v1_gitmirror_proto_goTypes = nil + file_gitmirror_v1_gitmirror_proto_depIdxs = nil +} diff --git a/gitmirror/proto/gen/gitmirror_grpc.pb.go b/gitmirror/proto/gen/gitmirror_grpc.pb.go new file mode 100644 --- /dev/null +++ b/gitmirror/proto/gen/gitmirror_grpc.pb.go @@ -0,0 +1,247 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.2 +// - protoc (unknown) +// source: gitmirror/v1/gitmirror.proto + +package gitmirrorv1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + GitMirrorService_CommitLog_FullMethodName = "/gitmirror.v1.GitMirrorService/CommitLog" + GitMirrorService_GetBlob_FullMethodName = "/gitmirror.v1.GitMirrorService/GetBlob" + GitMirrorService_Diff_FullMethodName = "/gitmirror.v1.GitMirrorService/Diff" + GitMirrorService_Interdiff_FullMethodName = "/gitmirror.v1.GitMirrorService/Interdiff" +) + +// GitMirrorServiceClient is the client API for GitMirrorService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type GitMirrorServiceClient interface { + CommitLog(ctx context.Context, in *CommitLogRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[CommitLogResponse], error) + GetBlob(ctx context.Context, in *GetBlobRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[BlobChunk], error) + Diff(ctx context.Context, in *DiffRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[FileDiff], error) + Interdiff(ctx context.Context, in *InterdiffRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[FileDiff], error) +} + +type gitMirrorServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewGitMirrorServiceClient(cc grpc.ClientConnInterface) GitMirrorServiceClient { + return &gitMirrorServiceClient{cc} +} + +func (c *gitMirrorServiceClient) CommitLog(ctx context.Context, in *CommitLogRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[CommitLogResponse], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &GitMirrorService_ServiceDesc.Streams[0], GitMirrorService_CommitLog_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &grpc.GenericClientStream[CommitLogRequest, CommitLogResponse]{ClientStream: stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type GitMirrorService_CommitLogClient = grpc.ServerStreamingClient[CommitLogResponse] + +func (c *gitMirrorServiceClient) GetBlob(ctx context.Context, in *GetBlobRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[BlobChunk], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &GitMirrorService_ServiceDesc.Streams[1], GitMirrorService_GetBlob_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &grpc.GenericClientStream[GetBlobRequest, BlobChunk]{ClientStream: stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type GitMirrorService_GetBlobClient = grpc.ServerStreamingClient[BlobChunk] + +func (c *gitMirrorServiceClient) Diff(ctx context.Context, in *DiffRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[FileDiff], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &GitMirrorService_ServiceDesc.Streams[2], GitMirrorService_Diff_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &grpc.GenericClientStream[DiffRequest, FileDiff]{ClientStream: stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type GitMirrorService_DiffClient = grpc.ServerStreamingClient[FileDiff] + +func (c *gitMirrorServiceClient) Interdiff(ctx context.Context, in *InterdiffRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[FileDiff], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &GitMirrorService_ServiceDesc.Streams[3], GitMirrorService_Interdiff_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &grpc.GenericClientStream[InterdiffRequest, FileDiff]{ClientStream: stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type GitMirrorService_InterdiffClient = grpc.ServerStreamingClient[FileDiff] + +// GitMirrorServiceServer is the server API for GitMirrorService service. +// All implementations must embed UnimplementedGitMirrorServiceServer +// for forward compatibility. +type GitMirrorServiceServer interface { + CommitLog(*CommitLogRequest, grpc.ServerStreamingServer[CommitLogResponse]) error + GetBlob(*GetBlobRequest, grpc.ServerStreamingServer[BlobChunk]) error + Diff(*DiffRequest, grpc.ServerStreamingServer[FileDiff]) error + Interdiff(*InterdiffRequest, grpc.ServerStreamingServer[FileDiff]) error + mustEmbedUnimplementedGitMirrorServiceServer() +} + +// UnimplementedGitMirrorServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedGitMirrorServiceServer struct{} + +func (UnimplementedGitMirrorServiceServer) CommitLog(*CommitLogRequest, grpc.ServerStreamingServer[CommitLogResponse]) error { + return status.Error(codes.Unimplemented, "method CommitLog not implemented") +} +func (UnimplementedGitMirrorServiceServer) GetBlob(*GetBlobRequest, grpc.ServerStreamingServer[BlobChunk]) error { + return status.Error(codes.Unimplemented, "method GetBlob not implemented") +} +func (UnimplementedGitMirrorServiceServer) Diff(*DiffRequest, grpc.ServerStreamingServer[FileDiff]) error { + return status.Error(codes.Unimplemented, "method Diff not implemented") +} +func (UnimplementedGitMirrorServiceServer) Interdiff(*InterdiffRequest, grpc.ServerStreamingServer[FileDiff]) error { + return status.Error(codes.Unimplemented, "method Interdiff not implemented") +} +func (UnimplementedGitMirrorServiceServer) mustEmbedUnimplementedGitMirrorServiceServer() {} +func (UnimplementedGitMirrorServiceServer) testEmbeddedByValue() {} + +// UnsafeGitMirrorServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to GitMirrorServiceServer will +// result in compilation errors. +type UnsafeGitMirrorServiceServer interface { + mustEmbedUnimplementedGitMirrorServiceServer() +} + +func RegisterGitMirrorServiceServer(s grpc.ServiceRegistrar, srv GitMirrorServiceServer) { + // If the following call panics, it indicates UnimplementedGitMirrorServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&GitMirrorService_ServiceDesc, srv) +} + +func _GitMirrorService_CommitLog_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(CommitLogRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GitMirrorServiceServer).CommitLog(m, &grpc.GenericServerStream[CommitLogRequest, CommitLogResponse]{ServerStream: stream}) +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type GitMirrorService_CommitLogServer = grpc.ServerStreamingServer[CommitLogResponse] + +func _GitMirrorService_GetBlob_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(GetBlobRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GitMirrorServiceServer).GetBlob(m, &grpc.GenericServerStream[GetBlobRequest, BlobChunk]{ServerStream: stream}) +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type GitMirrorService_GetBlobServer = grpc.ServerStreamingServer[BlobChunk] + +func _GitMirrorService_Diff_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(DiffRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GitMirrorServiceServer).Diff(m, &grpc.GenericServerStream[DiffRequest, FileDiff]{ServerStream: stream}) +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type GitMirrorService_DiffServer = grpc.ServerStreamingServer[FileDiff] + +func _GitMirrorService_Interdiff_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(InterdiffRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GitMirrorServiceServer).Interdiff(m, &grpc.GenericServerStream[InterdiffRequest, FileDiff]{ServerStream: stream}) +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type GitMirrorService_InterdiffServer = grpc.ServerStreamingServer[FileDiff] + +// GitMirrorService_ServiceDesc is the grpc.ServiceDesc for GitMirrorService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var GitMirrorService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "gitmirror.v1.GitMirrorService", + HandlerType: (*GitMirrorServiceServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "CommitLog", + Handler: _GitMirrorService_CommitLog_Handler, + ServerStreams: true, + }, + { + StreamName: "GetBlob", + Handler: _GitMirrorService_GetBlob_Handler, + ServerStreams: true, + }, + { + StreamName: "Diff", + Handler: _GitMirrorService_Diff_Handler, + ServerStreams: true, + }, + { + StreamName: "Interdiff", + Handler: _GitMirrorService_Interdiff_Handler, + ServerStreams: true, + }, + }, + Metadata: "gitmirror/v1/gitmirror.proto", +} diff --git a/gitmirror/proto/gitmirror/v1/gitmirror.proto b/gitmirror/proto/gitmirror/v1/gitmirror.proto new file mode 100644 --- /dev/null +++ b/gitmirror/proto/gitmirror/v1/gitmirror.proto @@ -0,0 +1,135 @@ +syntax = "proto3"; + +package gitmirror.v1; + +import "google/protobuf/timestamp.proto"; + +option go_package = "tangled.org/core/gitmirror/proto/gen;gitmirrorv1"; + +service GitMirrorService { + rpc CommitLog(CommitLogRequest) returns (stream CommitLogResponse); + rpc GetBlob(GetBlobRequest) returns (stream BlobChunk); + rpc Diff(DiffRequest) returns (stream FileDiff); + rpc Interdiff(InterdiffRequest) returns (stream FileDiff); +} + +message CommitLogRequest { + // repo DID + string repo = 1; + // Ranges to include in the git log (revspec, "A..B", "A...B", etc.). + // At least one range, or all_refs must be specified. + repeated bytes ranges = 2; + // If true, all refs are searched for commits. + // Must not be true when ranges are given. + bool all_refs = 3; + // After is an optional parameter to specify the earliest commit to consider. + google.protobuf.Timestamp after = 4; + // Before is an optional parameter to specify the latest commit to consider. + google.protobuf.Timestamp before = 5; + // MaxCommits is an optional parameter to specify the maximum number of commits + // to return. If max_commits is 0, all commits that match the criteria will be + // returned. + uint32 max_commits = 6; + // Skip is an optional parameter to specify the number of commits to skip. + // This can be used to implement a poor mans pagination. + uint32 skip = 7; +} +message CommitLogResponse { + repeated GitCommit commits = 1; +} + +message GetBlobRequest { + // repo carries the DID as a string. + string repo = 1; + // oid is the blob object id (hex). + string oid = 2; +} +message BlobChunk { + bytes data = 1; +} + +message DiffRequest { + // repo carries the DID as a string; parsing/validation is an impl detail. + string repo = 1; + // base_rev_spec/head_rev_spec are commit-ish ref strings. + // base_rev_spec is optional; when unset, diff head against head's first parent. + optional bytes base_rev_spec = 2; + bytes head_rev_spec = 3; + ComparisonType comparison_type = 4; + + enum ComparisonType { + COMPARISON_TYPE_UNSPECIFIED = 0; + // Corresponds to the BASE..HEAD syntax that only returns any commits that + // are in HEAD but not in BASE. + COMPARISON_TYPE_ONLY_IN_HEAD = 1; + // Corresponds to the BASE...HEAD syntax that returns any commits that are + // not in both BASE and HEAD. + COMPARISON_TYPE_INTERSECTION = 2; + } +} + +message InterdiffRequest { + // repo carries the DID as a string. + string repo = 1; + // Old patch range (from_base..from_head) and new patch range (to_base..to_head). + bytes from_base = 2; + bytes from_head = 3; + bytes to_base = 4; + bytes to_head = 5; +} + +// Diff and Interdiff stream one FileDiff per changed file. + +message FileContent { + string path = 1; + string oid = 2; + uint64 size = 3; + bool is_binary = 4; + bool is_submodule = 5; + // Raw file bytes, inlined when the oid is not fetchable by the client + // (e.g. interdiff) + optional bytes content = 6; +} + +// One aligned line pair; a side is absent (None) when unset. +message LinePair { + optional uint32 lhs = 1; + optional uint32 rhs = 2; +} + +message Hunk { + // LineNumber (u32) sets containing novel content per side. + repeated uint32 novel_lhs = 1; + repeated uint32 novel_rhs = 2; + repeated LinePair lines = 3; +} + +// Number of bytes per side when the files differ (Rust Option<(usize, usize)>). +message ByteChanges { + uint64 lhs = 1; + uint64 rhs = 2; +} + +message FileDiff { + FileContent lhs_src = 1; + FileContent rhs_src = 2; + repeated Hunk hunks = 3; + optional ByteChanges has_byte_changes = 4; + bool has_syntactic_changes = 5; + // TODO: lhs_positions & rhs_positions +} + +message GitCommit { + string oid = 1; + GitSignature author = 2; + GitSignature committer = 3; + bytes message = 4; + repeated string parents = 5; + map extra_headers = 6; +} + +message GitSignature { + bytes name = 1; + bytes email = 2; + google.protobuf.Timestamp date = 3; +} diff --git a/gitmirror/src/diff.rs b/gitmirror/src/diff.rs new file mode 100644 --- /dev/null +++ b/gitmirror/src/diff.rs @@ -0,0 +1,370 @@ +use gix::ObjectId; +use gix::bstr::ByteSlice as _; +use gix::objs::tree::EntryKind; +use line_numbers::{LineNumber, SingleLineSpan}; +use rustc_hash::FxHashSet; +use tracing::info; + +/// Blobs larger than this are treated as binary. +const LARGE_FILE_THRESHOLD_BYTES: u64 = 1024 * 1024; + +#[derive(Debug)] +pub struct FileContent { + pub(crate) path: String, + pub(crate) oid: String, + pub(crate) size: usize, + pub(crate) is_binary: bool, + pub(crate) is_submodule: bool, + /// Raw file bytes, inlined when the `oid` is not fetchable by the client (interdiff's + /// synthetic rebased tree). `None` for ordinary diffs and for binary/oversized blobs. + pub(crate) content: Option>, +} + +impl From for crate::protocol::v1::FileContent { + fn from(f: FileContent) -> Self { + Self { + path: f.path, + oid: f.oid, + size: f.size as u64, + is_binary: f.is_binary, + is_submodule: f.is_submodule, + content: f.content, + } + } +} + +#[derive(Debug, Clone)] +pub struct Hunk { + pub(crate) novel_lhs: FxHashSet, + pub(crate) novel_rhs: FxHashSet, + pub(crate) lines: Vec<(Option, Option)>, +} + +impl From for crate::protocol::v1::Hunk { + fn from(h: Hunk) -> Self { + // Sort the novel line sets for deterministic output. + let sorted = |set: FxHashSet| -> Vec { + let mut v: Vec = set.into_iter().map(|n| n.0).collect(); + v.sort_unstable(); + v + }; + Self { + novel_lhs: sorted(h.novel_lhs), + novel_rhs: sorted(h.novel_rhs), + lines: h + .lines + .into_iter() + .map(|(lhs, rhs)| crate::protocol::v1::LinePair { + lhs: lhs.map(|n| n.0), + rhs: rhs.map(|n| n.0), + }) + .collect(), + } + } +} + +/// A matched token (an atom, a delimiter, or a comment word). +#[derive(PartialEq, Eq, Debug, Clone)] +pub enum MatchKind { + // TBD +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MatchedPos { + pub(crate) kind: MatchKind, + pub(crate) pos: SingleLineSpan, +} + +#[derive(Debug)] +pub struct Diff { + pub(crate) lhs_src: FileContent, + pub(crate) rhs_src: FileContent, + pub(crate) hunks: Vec, + + #[allow(unused)] + pub(crate) lhs_positions: Vec, + #[allow(unused)] + pub(crate) rhs_positions: Vec, + + /// If the two files do not have exactly the same bytes, the + /// number of bytes in each file. + pub(crate) has_byte_changes: Option<(usize, usize)>, + pub(crate) has_syntactic_changes: bool, +} + +impl From for crate::protocol::v1::FileDiff { + fn from(d: Diff) -> Self { + Self { + lhs_src: Some(d.lhs_src.into()), + rhs_src: Some(d.rhs_src.into()), + hunks: d.hunks.into_iter().map(Into::into).collect(), + has_byte_changes: d.has_byte_changes.map(|(lhs, rhs)| { + crate::protocol::v1::ByteChanges { + lhs: lhs as u64, + rhs: rhs as u64, + } + }), + has_syntactic_changes: d.has_syntactic_changes, + } + } +} + +/// Diff two trees, yielding one [`Diff`] per changed file (line-level [`Hunk`]s included). +/// +/// Returns an iterator of `anyhow::Result`: per-file diffing can fail independently, so +/// each item is fallible. The consumer drives it lazily and can stop at any point by dropping +/// the iterator (e.g. when the gRPC client hangs up). +pub fn diff<'repo>( + repo: &'repo gix::Repository, + old_tree: &gix::Tree<'_>, + new_tree: &gix::Tree<'_>, + embed_content: bool, +) -> anyhow::Result> { + let changes = repo.diff_tree_to_tree(Some(old_tree), Some(new_tree), None)?; + let mut cache = repo.diff_resource_cache_for_tree_diff()?; + cache.filter.options.large_file_threshold_bytes = LARGE_FILE_THRESHOLD_BYTES; + Ok(DiffIter { + repo, + changes: changes.into_iter(), + cache, + embed_content, + }) +} + +/// Lazy iterator over per-file [`Diff`]s. Created by [`diff`]. +pub struct DiffIter<'repo> { + repo: &'repo gix::Repository, + changes: std::vec::IntoIter, + cache: gix::diff::blob::Platform, + embed_content: bool, +} + +impl Iterator for DiffIter<'_> { + type Item = anyhow::Result; + + fn next(&mut self) -> Option { + loop { + let change = self.changes.next()?; + let (old, new) = change_sides(&change); + // Trees are recursed into by gix and never diffed directly; skip defensively. + if old.is_kind(EntryKind::Tree) || new.is_kind(EntryKind::Tree) { + continue; + } + // Submodules (gitlinks) can't go through the blob pipeline — their commit lives in + // another repo. Emit the commit oids directly for the caller to compare. + if old.is_kind(EntryKind::Commit) || new.is_kind(EntryKind::Commit) { + return Some(Ok(submodule_diff(self.repo, &change, old, new))); + } + let result = build_diff(self.repo, &change, &mut self.cache, self.embed_content); + self.cache.clear_resource_cache_keep_allocation(); + return Some(result); + } + } +} + +/// One side (old or new) of a change. +enum Side { + Absent, + Present { oid: ObjectId, kind: EntryKind }, +} + +impl Side { + fn is_kind(&self, wanted: EntryKind) -> bool { + matches!(self, Side::Present { kind, .. } if *kind == wanted) + } +} + +/// Decompose a change into its `(old, new)` sides regardless of variant. +fn change_sides(change: &gix::object::tree::diff::ChangeDetached) -> (Side, Side) { + use gix::object::tree::diff::ChangeDetached as C; + let present = |oid, mode: gix::object::tree::EntryMode| Side::Present { + oid, + kind: mode.kind(), + }; + match change { + C::Addition { id, entry_mode, .. } => (Side::Absent, present(*id, *entry_mode)), + C::Deletion { id, entry_mode, .. } => (present(*id, *entry_mode), Side::Absent), + C::Modification { + previous_id, + previous_entry_mode, + id, + entry_mode, + .. + } => ( + present(*previous_id, *previous_entry_mode), + present(*id, *entry_mode), + ), + C::Rewrite { + source_id, + source_entry_mode, + id, + entry_mode, + .. + } => ( + present(*source_id, *source_entry_mode), + present(*id, *entry_mode), + ), + } +} + +/// Build a [`Diff`] for a submodule (gitlink) change: no hunks, both sides carry their commit +/// oid with `is_submodule` set. The caller compares the oids as submodule pointers. +fn submodule_diff( + repo: &gix::Repository, + change: &gix::object::tree::diff::ChangeDetached, + old: Side, + new: Side, +) -> Diff { + let path = change.location().to_string(); + let side = |s: Side| FileContent { + path: path.clone(), + oid: match s { + Side::Absent => ObjectId::null(repo.object_hash()).to_string(), + Side::Present { oid, .. } => oid.to_string(), + }, + size: 0, + is_binary: false, + is_submodule: true, + content: None, + }; + Diff { + lhs_src: side(old), + rhs_src: side(new), + hunks: Vec::new(), + lhs_positions: Vec::new(), + rhs_positions: Vec::new(), + has_byte_changes: None, + has_syntactic_changes: false, + } +} + +/// Build the [`Diff`] for a single changed file. +fn build_diff( + repo: &gix::Repository, + change: &gix::object::tree::diff::ChangeDetached, + cache: &mut gix::diff::blob::Platform, + embed_content: bool, +) -> anyhow::Result { + use gix::diff::blob::platform::prepare_diff::Operation; + use gix::diff::blob::platform::resource::Data; + use gix::diff::blob::{Diff as BlobDiff, InternedInput}; + use gix::prelude::TreeDiffChangeExt as _; + + let change = change.attach(repo, repo); + let blob = change.diff(cache)?; + let out = blob.resource_cache.prepare_diff()?; + + // Per-side file metadata, derived from the prepared resources. + let side = + |res: &gix::diff::blob::platform::Resource<'_>, embed_content: bool| -> FileContent { + let (size, is_binary, content) = match res.data { + // Only textual buffers carry inlinable bytes; binary/missing sides fall back to oid. + Data::Buffer { buf, .. } => (buf.len(), false, embed_content.then(|| buf.to_vec())), + Data::Binary { size } => (size as usize, true, None), + Data::Missing => (0, false, None), + }; + FileContent { + path: res.rela_path.to_string(), + oid: res.id.to_string(), + size, + is_binary, + is_submodule: false, + content, + } + }; + let mut lhs_src = side(&out.old, embed_content); + let mut rhs_src = side(&out.new, false); + + let has_byte_changes = if lhs_src.size == rhs_src.size && out.old.id == out.new.id { + None + } else { + Some((lhs_src.size, rhs_src.size)) + }; + + let hunks = match out.operation { + Operation::InternalDiff { algorithm } => { + let input = InternedInput::new(out.old.intern_source(), out.new.intern_source()); + let mut d = BlobDiff::compute(algorithm, &input); + d.postprocess_lines(&input); + d.hunks() + .map(|h| { + let before: Vec = h.before.clone().map(LineNumber).collect(); + let after: Vec = h.after.clone().map(LineNumber).collect(); + let n = before.len().max(after.len()); + let lines = (0..n) + .map(|i| (before.get(i).copied(), after.get(i).copied())) + .collect(); + Hunk { + novel_lhs: before.iter().copied().collect(), + novel_rhs: after.iter().copied().collect(), + lines, + } + }) + .collect() + } + Operation::SourceOrDestinationIsBinary => { + lhs_src.is_binary = true; + rhs_src.is_binary = true; + Vec::new() + } + Operation::ExternalCommand { .. } => unreachable!("we disabled that"), + }; + + Ok(Diff { + lhs_src, + rhs_src, + hunks, + lhs_positions: Vec::new(), + rhs_positions: Vec::new(), + has_byte_changes, + has_syntactic_changes: false, + }) +} + +/// Compute the two trees whose diff is the interdiff between an old patch +/// version (`from_base..from_head`) and a new one (`to_base..to_head`). +/// +/// Rebase `from_base..from_head` onto `to_base` and return rebased tree so +/// caller can diff between `rebased_tree` and `to_head.tree` +pub fn prepare_interdiff<'repo>( + repo: &'repo gix::Repository, + (from_base_id, from_head_id): (ObjectId, ObjectId), + to_base_id: ObjectId, +) -> anyhow::Result> { + let from_head = repo.find_commit(from_head_id)?; + + let from_base = repo.find_commit(from_base_id)?; + let to_base = repo.find_commit(to_base_id)?; + + // Endpoint trees for the 3-way merge. + let from_base_tree = from_base.tree_id()?.detach(); + let from_head_tree = from_head.tree_id()?.detach(); + let to_base_tree = to_base.tree_id()?.detach(); + + // Rebase = replay (from_base_tree -> from_head_tree) onto to_base_tree: + // ancestor = from_base_tree, ours = to_base_tree, theirs = from_head_tree. + let options = repo.tree_merge_options()?; + let labels = gix::merge::blob::builtin_driver::text::Labels { + ancestor: Some("from.base".as_bytes().as_bstr()), + current: Some("to.base".as_bytes().as_bstr()), + other: Some("from.head".as_bytes().as_bstr()), + }; + let mut outcome = repo.merge_trees( + from_base_tree, + to_base_tree, + from_head_tree, + labels, + options, + )?; + + if !outcome.conflicts.is_empty() { + info!( + conflicts = outcome.conflicts.len(), + "interdiff rebase produced conflicts; tree contains conflict markers" + ); + } + + let rebased_id = outcome.tree.write()?.detach(); + let rebased_tree = repo.find_tree(rebased_id)?; + Ok(rebased_tree) +} diff --git a/gitmirror/src/main.rs b/gitmirror/src/main.rs new file mode 100644 --- /dev/null +++ b/gitmirror/src/main.rs @@ -0,0 +1,45 @@ +use std::net::SocketAddr; +use std::path::PathBuf; + +use clap::{Parser, Subcommand}; + +mod diff; +mod protocol; +mod service; + +#[derive(Parser)] +#[command(name = "gitmirror", about = "Git mirror gRPC service")] +struct Cli { + #[command(subcommand)] + cmd: Command, +} + +#[derive(Subcommand)] +enum Command { + /// Run the gRPC server. + Serve(ServeArgs), +} + +#[derive(clap::Args)] +struct ServeArgs { + /// Address to bind the gRPC server to. + #[arg(long, env = "GITMIRROR_ADDR", default_value = "127.0.0.1:9000")] + addr: SocketAddr, + + /// Base directory holding bare mirror repos, one per DID (/). + #[arg(long, env = "GITMIRROR_REPO_BASE", default_value = "repos")] + repo_base: PathBuf, +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + tracing_subscriber::fmt() + .with_env_filter( + tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()), + ) + .init(); + + match Cli::parse().cmd { + Command::Serve(args) => service::serve(args.addr, args.repo_base).await, + } +} diff --git a/gitmirror/src/protocol.rs b/gitmirror/src/protocol.rs new file mode 100644 --- /dev/null +++ b/gitmirror/src/protocol.rs @@ -0,0 +1,3 @@ +pub mod v1 { + tonic::include_proto!("gitmirror.v1"); +} diff --git a/gitmirror/src/service.rs b/gitmirror/src/service.rs new file mode 100644 --- /dev/null +++ b/gitmirror/src/service.rs @@ -0,0 +1,362 @@ +use std::net::SocketAddr; +use std::path::PathBuf; +use std::pin::Pin; + +use gix::bstr::ByteSlice as _; +use gix::revision::plumbing::Spec as RevSpec; +use gix::revision::walk::Sorting; +use tokio::sync::mpsc; +use tokio_stream::Stream; +use tokio_stream::wrappers::ReceiverStream; +use tonic::transport::Server; +use tonic::{Request, Response, Status}; +use tracing::{debug, info}; + +use crate::diff::{self, prepare_interdiff}; +use crate::protocol::v1::diff_request::ComparisonType; +use crate::protocol::v1::git_mirror_service_server::{GitMirrorService, GitMirrorServiceServer}; +use crate::protocol::v1::{ + BlobChunk, CommitLogRequest, CommitLogResponse, DiffRequest, FileDiff, GetBlobRequest, + GitCommit, GitSignature, InterdiffRequest, +}; + +type FileDiffStream = Pin> + Send>>; +type BlobChunkStream = Pin> + Send>>; +type CommitLogResponseStream = + Pin> + Send>>; + +/// Blob bytes are streamed in chunks of this size. +const BLOB_CHUNK_SIZE: usize = 64 * 1024; +/// GitCommits are streamed in batches of this size. +const COMMIT_LOG_BATCH_SIZE: usize = 128; + +struct GitMirror { + repo_base: PathBuf, +} + +impl GitMirror { + fn open_repo(&self, did: &str) -> Result { + if !did.starts_with("did:") { + return Err(Status::invalid_argument("repo must be a DID")); + } + gix::open(self.repo_base.join(did)) + .map_err(|e| Status::not_found(format!("repo not found: {e}"))) + } +} + +#[tonic::async_trait] +impl GitMirrorService for GitMirror { + type DiffStream = FileDiffStream; + + async fn diff( + &self, + request: Request, + ) -> Result, Status> { + let req = request.into_inner(); + let repo = self.open_repo(&req.repo)?; + // Resolve refs up-front so bad input surfaces as the RPC status, then move the + // owned repo + oids into a blocking task for the (synchronous) diff loop. + let base_oid = req + .base_rev_spec + .as_deref() + .map(|b| resolve_commit_oid(&repo, b)) + .transpose()?; + let head_oid = resolve_commit_oid(&repo, &req.head_rev_spec)?; + let comparison_type = + ComparisonType::try_from(req.comparison_type).unwrap_or(ComparisonType::Unspecified); + let safe = repo.into_sync(); + + let (tx, rx) = mpsc::channel::>(16); + tokio::task::spawn_blocking(move || { + let repo = safe.to_thread_local(); + + let run = || -> anyhow::Result<()> { + let head_commit = repo.find_commit(head_oid)?; + let old = match base_oid { + Some(base_oid) => match comparison_type { + ComparisonType::OnlyInHead => { + repo.find_tree(repo.find_commit(base_oid)?.tree_id()?)? + } + ComparisonType::Unspecified | ComparisonType::Intersection => { + let merge_base = repo.merge_base(base_oid, head_oid)?.detach(); + repo.find_tree(repo.find_commit(merge_base)?.tree_id()?)? + } + }, + None => match head_commit.parent_ids().next() { + Some(parent_id) => { + repo.find_tree(repo.find_commit(parent_id.detach())?.tree_id()?)? + } + None => repo.empty_tree(), + }, + }; + let new = repo.find_tree(head_commit.tree_id()?)?; + for item in diff::diff(&repo, &old, &new, false)? { + if tx.blocking_send(Ok(item?.into())).is_err() { + break; // client hung up + } + } + Ok(()) + }; + if let Err(e) = run() { + let _ = tx.blocking_send(Err(Status::internal(e.to_string()))); + } + }); + + Ok(Response::new(Box::pin(ReceiverStream::new(rx)))) + } + + type InterdiffStream = FileDiffStream; + + async fn interdiff( + &self, + request: Request, + ) -> Result, Status> { + let req = request.into_inner(); + let repo = self.open_repo(&req.repo)?; + let from_base_id = resolve_commit_oid(&repo, &req.from_base)?; + let from_head_id = resolve_commit_oid(&repo, &req.from_head)?; + let to_base_id = resolve_commit_oid(&repo, &req.to_base)?; + let to_head_id = resolve_commit_oid(&repo, &req.to_head)?; + let safe = repo.into_sync(); + + let (tx, rx) = mpsc::channel::>(16); + tokio::task::spawn_blocking(move || { + let repo = safe.to_thread_local(); + let run = || -> anyhow::Result<()> { + let to_head = repo.find_commit(to_head_id)?; + let to_head_tree = to_head.tree()?; + let rebased_tree = + prepare_interdiff(&repo, (from_base_id, from_head_id), to_base_id)?; + for item in diff::diff(&repo, &rebased_tree, &to_head_tree, true)? { + if tx.blocking_send(Ok(item?.into())).is_err() { + break; // client hung up + } + } + Ok(()) + }; + if let Err(e) = run() { + let _ = tx.blocking_send(Err(Status::internal(e.to_string()))); + } + }); + + Ok(Response::new(Box::pin(ReceiverStream::new(rx)))) + } + + type GetBlobStream = BlobChunkStream; + + async fn get_blob( + &self, + request: Request, + ) -> Result, Status> { + let req = request.into_inner(); + let repo = self.open_repo(&req.repo)?; + let oid = gix::ObjectId::from_hex(req.oid.as_bytes()) + .map_err(|e| Status::invalid_argument(format!("bad oid '{}': {e}", req.oid)))?; + let safe = repo.into_sync(); + + let (tx, rx) = mpsc::channel::>(16); + tokio::task::spawn_blocking(move || { + let repo = safe.to_thread_local(); + let run = || -> anyhow::Result<()> { + let blob = repo.find_object(oid)?.try_into_blob()?; + for chunk in blob.data.chunks(BLOB_CHUNK_SIZE) { + let msg = BlobChunk { + data: chunk.to_vec(), + }; + if tx.blocking_send(Ok(msg)).is_err() { + break; // client hung up + } + } + Ok(()) + }; + if let Err(e) = run() { + let _ = tx.blocking_send(Err(Status::internal(e.to_string()))); + } + }); + + Ok(Response::new(Box::pin(ReceiverStream::new(rx)))) + } + + type CommitLogStream = CommitLogResponseStream; + + async fn commit_log( + &self, + request: Request, + ) -> Result, Status> { + let req = request.into_inner(); + debug!( + repo = %req.repo, + all_refs = req.all_refs, + ranges = ?req.ranges, + after = ?req.after, + before = ?req.before, + max_commits = req.max_commits, + skip = req.skip, + "commit_log request", + ); + if req.all_refs != req.ranges.is_empty() { + return Err(Status::invalid_argument( + "specify exactly one of `ranges` or `all_refs`", + )); + } + let repo = self.open_repo(&req.repo)?; + let safe = repo.into_sync(); + + let (tx, rx) = mpsc::channel::>(16); + tokio::task::spawn_blocking(move || { + let repo = safe.to_thread_local(); + let run = || -> anyhow::Result<()> { + let after = req.after.map(|t| t.seconds); + let before = req.before.map(|t| t.seconds); + let max_commits = req.max_commits as usize; + let mut skip = req.skip as usize; + let mut sent = 0usize; + let mut batch = Vec::with_capacity(COMMIT_LOG_BATCH_SIZE); + + for info in commit_log_walk(&repo, &req)? { + let info = info?; + let commit_time = info.commit_time(); + if before.is_some_and(|b| commit_time > b) { + continue; + } + if after.is_some_and(|a| commit_time < a) { + break; // newest-first order: nothing older will match either + } + if skip > 0 { + skip -= 1; + continue; + } + if max_commits != 0 && sent >= max_commits { + break; + } + batch.push(GitCommit::try_from(&info.object()?)?); + sent += 1; + if batch.len() == COMMIT_LOG_BATCH_SIZE { + let msg = CommitLogResponse { + commits: std::mem::take(&mut batch), + }; + if tx.blocking_send(Ok(msg)).is_err() { + return Ok(()); // client hung up + } + } + } + if !batch.is_empty() { + let _ = tx.blocking_send(Ok(CommitLogResponse { commits: batch })); + } + Ok(()) + }; + if let Err(e) = run() { + let _ = tx.blocking_send(Err(Status::internal(e.to_string()))); + } + }); + + Ok(Response::new(Box::pin(ReceiverStream::new(rx)))) + } +} + +/// Resolve a commit-ish ref string (oid, short oid, branch/tag) to its commit oid. +fn resolve_commit_oid(repo: &gix::Repository, spec: &[u8]) -> Result { + let commit = repo + .rev_parse_single(spec.as_bstr()) + .map_err(|e| { + Status::invalid_argument(format!("bad rev '{}': {e}", String::from_utf8_lossy(spec))) + })? + .object() + .map_err(|e| Status::internal(e.to_string()))? + .peel_to_commit() + .map_err(|e| Status::internal(e.to_string()))?; + Ok(commit.id) +} + +/// Resolve tips/hidden commits from a [`CommitLogRequest`] and return the resulting gix walk, +/// sorted newest-first to match `git log`'s default order. +fn commit_log_walk<'repo>( + repo: &'repo gix::Repository, + req: &CommitLogRequest, +) -> anyhow::Result> { + let mut tips = Vec::new(); + let mut hidden = Vec::new(); + + if req.all_refs { + for r in repo.references()?.all()? { + let mut r = r.map_err(|e| anyhow::anyhow!(e))?; + if let Ok(commit) = r.peel_to_commit() { + tips.push(commit.id); + } + } + } else { + for range in &req.ranges { + let revspec = repo.rev_parse(range.as_bstr())?; + let spec = revspec.detach(); + match spec { + RevSpec::Include(id) => tips.push(id), + RevSpec::Range { from, to } => { + tips.push(to); + hidden.push(from); + } + _ => { + anyhow::bail!("The spec isn't currently supported: {spec:?}") + } + } + } + } + + Ok(repo + .rev_walk(tips) + .sorting(Sorting::ByCommitTime(Default::default())) + .with_hidden(hidden) + .all()?) +} + +impl TryFrom<&gix::Commit<'_>> for GitCommit { + type Error = anyhow::Error; + + fn try_from(commit: &gix::Commit<'_>) -> anyhow::Result { + let decoded = commit.decode()?; + Ok(GitCommit { + oid: commit.id.to_string(), + author: Some(decoded.author()?.try_into()?), + committer: Some(decoded.committer()?.try_into()?), + message: decoded.message.to_vec(), + parents: decoded + .parents + .into_iter() + .map(|id| id.to_string()) + .collect(), + extra_headers: decoded + .extra_headers + .iter() + .map(|(k, v)| (k.to_string(), v.to_string())) + .collect(), + }) + } +} + +impl TryFrom> for GitSignature { + type Error = anyhow::Error; + + fn try_from(sig: gix::actor::SignatureRef<'_>) -> anyhow::Result { + let time = sig.time()?; + Ok(GitSignature { + name: sig.name.to_vec(), + email: sig.email.to_vec(), + date: Some(prost_types::Timestamp { + seconds: time.seconds, + nanos: 0, + }), + }) + } +} + +/// Build the gRPC service and serve it until the process is stopped. +pub async fn serve(addr: SocketAddr, repo_base: PathBuf) -> anyhow::Result<()> { + let service = GitMirror { repo_base }; + info!(addr = %addr, "gitmirror gRPC server listening"); + + Server::builder() + .add_service(GitMirrorServiceServer::new(service)) + .serve(addr) + .await?; + + Ok(()) +} diff --git a/localinfra/Caddyfile b/localinfra/Caddyfile --- a/localinfra/Caddyfile +++ b/localinfra/Caddyfile @@ -53,6 +53,16 @@ tls internal reverse_proxy knotmirror:7000 } +gitmirror.tngl.boltless.dev { + tls internal + # gitmirror (tonic) serves h2c; Caddy defaults to HTTP/1.1 to the backend. + reverse_proxy gitmirror:9000 { + transport http { + versions h2c 2 + } + } +} + # zoekt (internal service) zoekt.tngl.boltless.dev { tls internal diff --git a/localinfra/gitmirror.Dockerfile b/localinfra/gitmirror.Dockerfile new file mode 100644 --- /dev/null +++ b/localinfra/gitmirror.Dockerfile @@ -0,0 +1,29 @@ +# Development only. Not for production use. + +FROM rust:1.96-bookworm AS build + +RUN apt-get update && apt-get install -y --no-install-recommends protobuf-compiler libprotobuf-dev \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /src +COPY . . + +# Cache mounts on the cargo registry + target dir. The binary is copied out within the same +# RUN because the target cache mount is unmounted afterwards (a later COPY --from could not +# see it). +RUN --mount=type=cache,target=/usr/local/cargo/registry \ + --mount=type=cache,target=/src/target \ + cargo build --release -p gitmirror \ + && cp /src/target/release/gitmirror /usr/local/bin/gitmirror + +FROM debian:bookworm-slim + +RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates tini \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=build /usr/local/bin/gitmirror /usr/local/bin/gitmirror + +EXPOSE 9000 + +ENTRYPOINT ["/usr/bin/tini", "--"] +CMD ["sh", "-c", "if [ -f /usr/local/share/ca-certificates/caddy.crt ]; then update-ca-certificates; fi && exec /usr/local/bin/gitmirror serve"] -- tangled.sh