From 3c031cedf69225792e3cca4f5e36dbdba57d5a85 Mon Sep 17 00:00:00 2001 From: "Aashutosh A." Date: Wed, 18 Feb 2026 06:55:49 +0100 Subject: [PATCH 1/8] chore: remove dead code folder --- src/main.py | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 src/main.py diff --git a/src/main.py b/src/main.py deleted file mode 100644 index 4e3d766..0000000 --- a/src/main.py +++ /dev/null @@ -1,5 +0,0 @@ -# &begin[test] -def test(): ... - - -# &end[test] From 1b4233442e70ead7d5754d4ecef80e403f9b76b3 Mon Sep 17 00:00:00 2001 From: "Aashutosh A." Date: Wed, 18 Feb 2026 07:10:17 +0100 Subject: [PATCH 2/8] feat: initialize rust setup and cli structure --- .gitignore | 6 +- Cargo.lock | 676 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 13 + README.md | 28 +++ src/lib.rs | 6 + src/main.rs | 30 +++ 6 files changed, 758 insertions(+), 1 deletion(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/lib.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore index 7bf286f..ee5854e 100644 --- a/.gitignore +++ b/.gitignore @@ -161,4 +161,8 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ -.vscode/ \ No newline at end of file +.vscode/ + +# Added by cargo + +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..949d7a9 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,676 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clap" +version = "4.5.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5caf74d17c3aec5495110c34cc3f78644bfa89af6c8993ed4de2790e49b6499" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "370daa45065b80218950227371916a1633217ae42b2715b2287b606dcd618e24" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "git-varcs" +version = "2.0.0-alpha.0" +dependencies = [ + "anyhow", + "clap", + "git2", +] + +[[package]] +name = "git2" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom", + "libc", +] + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "libgit2-sys" +version = "0.18.3+1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9b3acc4b91781bb0b3386669d325163746af5f6e4f73e6d2d630e09a35f3487" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", +] + +[[package]] +name = "libssh2-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "220e4f05ad4a218192533b300327f5150e809b54c4ec83b5a1d91833601811b9" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..27b37e7 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "git-varcs" +version = "2.0.0-alpha.0" +edition = "2024" + +[[bin]] +name = "git-vms" +path = "src/main.rs" + +[dependencies] +anyhow = "1.0.101" +clap = { version = "4.5.59", features = ["derive"] } +git2 = "0.20.4" diff --git a/README.md b/README.md index 4df1e3c..d7a4cc3 100644 --- a/README.md +++ b/README.md @@ -160,3 +160,31 @@ For further details or to explore more usage options, refer to the [Typer Docume ## Development 1. Create a virtual environment and install both requirement-files. + +### Rust Components + +To build and run the rust components, use standard Cargo commands from the project root: + +```bash +# Build the project +cargo build + +# Run the project +cargo run +``` + +The binary will be compiled into `target/debug/git-vms`. + +**Symlinking Binaries** + +For rapid development, you can symlink the compiled binaries to a location in your `$PATH` so you can run them like any other git command: + +```bash +ln -s $(pwd)/target/debug/git-vms /path/to/your/bin/git-vms +``` + +After that, you can run the binary directly: + +```bash +git vms --version +``` diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..adc1f24 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,6 @@ +use anyhow::Result; +use git2::Repository; + +pub fn init(_repo: &Repository) -> Result<()> { + todo!("Initialize vms support in the git repo"); +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..748ac03 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,30 @@ +use anyhow::{Context, Result}; +use clap::{Parser, Subcommand}; +use git2::Repository; + +use git_varcs::init; + +#[derive(Parser)] +#[command(name = "git-vms", version)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + /// Initialize variant management support in the git repository + Init, +} + +fn main() -> Result<()> { + let args = Cli::parse(); + let repo = Repository::discover(".").context("Not in a git repository")?; + + match args.command { + Commands::Init => { + init(&repo).context("Failed to initialize VMS support")?; + } + } + Ok(()) +} From ff1bf42ed28bc0dcaf7fc0e4459e8096cd1eb7f2 Mon Sep 17 00:00:00 2001 From: "Aashutosh A." Date: Wed, 18 Feb 2026 10:28:50 +0100 Subject: [PATCH 3/8] feat: varcs init command --- src/lib.rs | 37 +++++++++++++++++++++++--- src/templates/hooks/prepare-commit-msg | 20 ++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 src/templates/hooks/prepare-commit-msg diff --git a/src/lib.rs b/src/lib.rs index adc1f24..ad9bb77 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,37 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use git2::Repository; +use std::fs; +use std::io::Write; +use std::os::unix::fs::OpenOptionsExt; -pub fn init(_repo: &Repository) -> Result<()> { - todo!("Initialize vms support in the git repo"); +pub fn init(repo: &Repository) -> Result<()> { + let pre_commit_file_template = include_str!("templates/hooks/prepare-commit-msg"); + let base_path = repo + .path() + .parent() + .context("Repository has no parent directory")?; + let config_path = base_path.join("varcs.toml"); + + if config_path.exists() { + println!("Config file already exists. Skipping..."); + } else { + fs::File::create(config_path).context("Failed to create config file")?; + } + + let hooks_folder = base_path.join(".git/hooks/"); + let target_pc_file = hooks_folder.join("prepare-commit-msg"); + if target_pc_file.exists() { + println!("Pre-commit file already exists. You may want to update it manually."); + } else { + let mut file = fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .mode(0o755) + .open(&target_pc_file) + .context("Faild to create pre-commit file")?; + file.write_all(pre_commit_file_template.as_bytes()) + .context("Failed to write to pre-commit file")?; + } + Ok(()) } diff --git a/src/templates/hooks/prepare-commit-msg b/src/templates/hooks/prepare-commit-msg new file mode 100644 index 0000000..9065b36 --- /dev/null +++ b/src/templates/hooks/prepare-commit-msg @@ -0,0 +1,20 @@ +#!/bin/bash + +COMMIT_MSG_FILE=$1 +COMMIT_SOURCE=$2 + +# Skip merge commits +if [[ "$COMMIT_SOURCE" == "merge" ]]; then + exit 0 +fi + +# Generate a UUID for now (replace with API call later) +CHANGE_ID=$(uuidgen) + +# Check if "Ticket:" is already present to avoid duplicate +if ! grep -q "^CHANGE_ID:" "$COMMIT_MSG_FILE"; then + # Append a blank line + ticket info at the end + echo -e "\nCHANGE_ID: $CHANGE_ID" >> "$COMMIT_MSG_FILE" +fi + +exit 0 From 71226b416eafd1a844f299eab2bb8b21c711de90 Mon Sep 17 00:00:00 2001 From: "Aashutosh A." Date: Wed, 18 Feb 2026 11:06:21 +0100 Subject: [PATCH 4/8] feat: add cherry-pick aware branch diff --- Cargo.lock | 45 ++++++++++++++++++ Cargo.toml | 1 + src/lib.rs | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/main.rs | 16 ++++++- 4 files changed, 188 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 949d7a9..b62227a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + [[package]] name = "anstream" version = "0.6.21" @@ -173,6 +182,7 @@ dependencies = [ "anyhow", "clap", "git2", + "regex", ] [[package]] @@ -372,6 +382,12 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + [[package]] name = "once_cell_polyfill" version = "1.70.2" @@ -441,6 +457,35 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" + [[package]] name = "serde" version = "1.0.228" diff --git a/Cargo.toml b/Cargo.toml index 27b37e7..aec2793 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,4 @@ path = "src/main.rs" anyhow = "1.0.101" clap = { version = "4.5.59", features = ["derive"] } git2 = "0.20.4" +regex = "1.12.3" diff --git a/src/lib.rs b/src/lib.rs index ad9bb77..9557816 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,12 @@ use anyhow::{Context, Result}; -use git2::Repository; +use git2::{BranchType, Commit, Repository, Sort}; +use regex::Regex; use std::fs; use std::io::Write; use std::os::unix::fs::OpenOptionsExt; +use std::collections::HashSet; + pub fn init(repo: &Repository) -> Result<()> { let pre_commit_file_template = include_str!("templates/hooks/prepare-commit-msg"); let base_path = repo @@ -35,3 +38,126 @@ pub fn init(repo: &Repository) -> Result<()> { } Ok(()) } + +pub fn diff(repo: &Repository, target: &str, reverse: bool) -> Result<()> { + let current_ref = repo.head()?; + let target_ref = repo + .find_branch(target, BranchType::Local) + .context("Specified target branch does not exist")? + .into_reference(); + + let (current_ref, target_ref) = if reverse { + (target_ref, current_ref) + } else { + (current_ref, target_ref) + }; + + let current_ref_name = current_ref + .name() + .context("Reference has no valid UTF-8 name")?; + let target_ref_name = target_ref + .name() + .context("Reference has no valid UTF-8 name")?; + + let exclude_set: HashSet<_> = extract_change_ids(repo, current_ref_name)?; + + let missing_commits = filter_missing_commits(repo, target_ref_name, &exclude_set)?; + + for commit in &missing_commits { + let summary = commit.summary().context("Error fetching commit summary")?; + println!("{:.7}\t{}", commit.id(), summary); + } + Ok(()) +} + +fn extract_change_ids(repo: &Repository, reference: &str) -> Result> { + let mut ids: HashSet = HashSet::new(); + + let mut revwalk = repo.revwalk()?; + revwalk.push_ref(reference)?; + revwalk.set_sorting(Sort::TOPOLOGICAL | Sort::TIME)?; + + for oid in revwalk { + let oid = oid?; + let commit = repo.find_commit(oid)?; + let message = commit + .message() + .context("Commit message is not valid UTF-8")?; + let change_id = extract_change_id(message) + .context("Failed to extract change-id from commit message")?; + + ids.insert(change_id); + } + Ok(ids) +} + +fn filter_missing_commits<'repo>( + repo: &'repo Repository, + ref_name: &str, + exclude_set: &HashSet, +) -> Result>> { + let mut missing: Vec = Vec::new(); + let mut revwalk = repo.revwalk()?; + revwalk.push_ref(ref_name)?; + revwalk.set_sorting(Sort::TOPOLOGICAL | Sort::TIME)?; + + for oid in revwalk { + let oid = oid?; + let commit = repo.find_commit(oid)?; + let message = commit.message().context("Commit has no message!")?; + let change_id = extract_change_id(message) + .context("Failed to extract change-id from commit message")?; + if !exclude_set.contains(&change_id) { + missing.push(commit); + } + } + Ok(missing) +} + +// TODO: building each time can be expensive. Look for an alternative +fn extract_change_id(msg: &str) -> Option { + let re = Regex::new(r"CHANGE_ID:\s*([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b)").unwrap(); + re.captures(msg) + .and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn extracts_valid_change_id() { + let msg = r#" +feat(api)!: send an email to the customer when a product is shipped + +CHANGE_ID: fec54ceb-74fe-4c6e-b467-cee066ffa268 +"#; + let result = extract_change_id(msg); + assert_eq!( + result, + Some("fec54ceb-74fe-4c6e-b467-cee066ffa268".to_string()) + ); + } + + #[test] + fn returns_none_when_no_change_id_present() { + let msg = r#" +feat(api)!: send an email to the customer when a product is shipped +"#; + + let result = extract_change_id(msg); + assert_eq!(result, None); + } + + #[test] + fn returns_none_for_invalid_uuid_format() { + let msg = r#" +feat(api)!: send an email to the customer when a product is shipped + +CHANGE_ID: fec54ceb-74fe-4c6e-b467-cee066ffa268abc +"#; + + let result = extract_change_id(msg); + assert_eq!(result, None); + } +} diff --git a/src/main.rs b/src/main.rs index 748ac03..f8059b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use anyhow::{Context, Result}; use clap::{Parser, Subcommand}; use git2::Repository; -use git_varcs::init; +use git_varcs::{diff, init}; #[derive(Parser)] #[command(name = "git-vms", version)] @@ -15,6 +15,14 @@ struct Cli { enum Commands { /// Initialize variant management support in the git repository Init, + /// List the commits exist in the target branch but not in the current branch + Diff { + /// The branch to compare against + target: String, + /// Swap the current branch and target when computing the diff + #[arg(short, long)] + reverse: bool, + }, } fn main() -> Result<()> { @@ -25,6 +33,12 @@ fn main() -> Result<()> { Commands::Init => { init(&repo).context("Failed to initialize VMS support")?; } + Commands::Diff { target, reverse } => { + diff(&repo, &target, reverse).context(format!( + "Failed to diff current branch against '{}'", + target + ))?; + } } Ok(()) } From 060e359d367d3a24a3a39583922aebff8869d9f3 Mon Sep 17 00:00:00 2001 From: "Aashutosh A." Date: Thu, 19 Feb 2026 10:37:09 +0100 Subject: [PATCH 5/8] wip!: variant derivation --- src/lib.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 13 ++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 9557816..a86704a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,6 +39,33 @@ pub fn init(repo: &Repository) -> Result<()> { Ok(()) } +pub fn derive(repo: &Repository, name: &str, features: &[String]) -> Result<()> { + println!("Deriving variant '{}' with features: {:?}", name, features); + // create a new orphan branch + // walk the commit graph + let mut revwalk = repo.revwalk()?; + revwalk.push_head()?; + revwalk.set_sorting(Sort::TOPOLOGICAL | Sort::TIME | Sort::REVERSE)?; + + let target_features: HashSet = features.iter().cloned().collect(); + for oid in revwalk { + let oid = oid?; + let commit = repo.find_commit(oid)?; + + let summary = commit.summary().context("Error extracting summary")?; + if let Some(feature) = extract_scope(summary) { + if target_features.contains(feature) { + println!("Applying commit: {} {}", oid, summary); + } + } + } + + // for each commit that is in the specified feature set, extract the delta + // put together the delta + // add a custom reference to the newly created branch to keep track of variants + Ok(()) +} + pub fn diff(repo: &Repository, target: &str, reverse: bool) -> Result<()> { let current_ref = repo.head()?; let target_ref = repo @@ -121,6 +148,12 @@ fn extract_change_id(msg: &str) -> Option { .and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())) } +fn extract_scope(commit: &str) -> Option<&str> { + let re = Regex::new(r"^(?P[a-z]+)(?:\((?P[^)]+)\))?(?P!)?:").unwrap(); + re.captures(commit) + .and_then(|caps| caps.name("scope").map(|m| m.as_str())) +} + #[cfg(test)] mod tests { use super::*; @@ -160,4 +193,27 @@ CHANGE_ID: fec54ceb-74fe-4c6e-b467-cee066ffa268abc let result = extract_change_id(msg); assert_eq!(result, None); } + + #[test] + fn extracts_scope_when_present() { + assert_eq!(extract_scope("feat(auth): add login"), Some("auth")); + } + + #[test] + fn extracts_scope_with_breaking_change() { + assert_eq!( + extract_scope("feat(api)!: change response format"), + Some("api") + ); + } + + #[test] + fn returns_none_when_no_scope() { + assert_eq!(extract_scope("fix: missing semicolon"), None); + } + + #[test] + fn does_not_match_invalid_format() { + assert_eq!(extract_scope("not a conventional commit"), None); + } } diff --git a/src/main.rs b/src/main.rs index f8059b8..c65199e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use anyhow::{Context, Result}; use clap::{Parser, Subcommand}; use git2::Repository; -use git_varcs::{diff, init}; +use git_varcs::{derive, diff, init}; #[derive(Parser)] #[command(name = "git-vms", version)] @@ -15,6 +15,14 @@ struct Cli { enum Commands { /// Initialize variant management support in the git repository Init, + /// Derive a new variant from the specified set of features + Derive { + /// The name of the variant + name: String, + /// The set of features to use for the derivation + #[arg(short, long = "feature", required = true)] + features: Vec, + }, /// List the commits exist in the target branch but not in the current branch Diff { /// The branch to compare against @@ -33,6 +41,9 @@ fn main() -> Result<()> { Commands::Init => { init(&repo).context("Failed to initialize VMS support")?; } + Commands::Derive { name, features } => { + derive(&repo, &name, &features).context("Failed to derive variant")?; + } Commands::Diff { target, reverse } => { diff(&repo, &target, reverse).context(format!( "Failed to diff current branch against '{}'", From c45f2a6d26d881b2fa6ca2b32e05bf21c00c1b76 Mon Sep 17 00:00:00 2001 From: "Aashutosh A." Date: Thu, 19 Feb 2026 14:03:11 +0100 Subject: [PATCH 6/8] wip!: orphan branch with initial commit for variant the initial commit will house metadata on the variant that is being derived like the author, the configs and so on. --- src/lib.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index a86704a..c43c4d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ use anyhow::{Context, Result}; -use git2::{BranchType, Commit, Repository, Sort}; +use git2::{BranchType, Commit, Repository, Signature, Sort}; use regex::Regex; use std::fs; use std::io::Write; @@ -42,6 +42,28 @@ pub fn init(repo: &Repository) -> Result<()> { pub fn derive(repo: &Repository, name: &str, features: &[String]) -> Result<()> { println!("Deriving variant '{}' with features: {:?}", name, features); // create a new orphan branch + let tree_oid = repo + .treebuilder(None)? + .write() + .context("Failed to create empty tree.")?; + let tree = repo.find_tree(tree_oid)?; + println!("Treeid: {:?}", tree_oid); + + let sig = Signature::now("user", "user@example.com")?; + + let ref_name = format!("refs/heads/variant/{name}"); + let commit_oid = repo + .commit( + Some(&ref_name), + &sig, + &sig, + "Variant initial commit\n", + &tree, + &[], + ) + .context("Failed to create initial commit for variant")?; + println!("Commit id: {:?}", commit_oid); + // walk the commit graph let mut revwalk = repo.revwalk()?; revwalk.push_head()?; From 30aa0713f93021e7e505e9819756b5d57333b5c7 Mon Sep 17 00:00:00 2001 From: "Aashutosh A." Date: Thu, 19 Feb 2026 14:30:18 +0100 Subject: [PATCH 7/8] wip!: add symbolic refs to variants --- src/lib.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index c43c4d7..91a35b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -85,6 +85,17 @@ pub fn derive(repo: &Repository, name: &str, features: &[String]) -> Result<()> // for each commit that is in the specified feature set, extract the delta // put together the delta // add a custom reference to the newly created branch to keep track of variants + let var_ref_name = format!("refs/variants/{name}"); + let target_ref = repo.find_reference(&ref_name)?; + let var_ref_target = target_ref.name().context("Branch has invalid UTF-8 name")?; + + repo.reference_symbolic( + &var_ref_name, + &var_ref_target, + false, + "initialized new variant", + ) + .context("Failed to create symbolic ref to variant")?; Ok(()) } From caed58d4cdf3ca0fb8702852c166279ed2b69d52 Mon Sep 17 00:00:00 2001 From: "Aashutosh A." Date: Fri, 20 Feb 2026 07:23:40 +0100 Subject: [PATCH 8/8] wip!: add command to list all the variant present in the repo --- src/lib.rs | 21 +++++++++++++++++++++ src/main.rs | 11 ++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 91a35b5..7504188 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ use anyhow::{Context, Result}; +use clap::ValueEnum; use git2::{BranchType, Commit, Repository, Signature, Sort}; use regex::Regex; use std::fs; @@ -7,6 +8,11 @@ use std::os::unix::fs::OpenOptionsExt; use std::collections::HashSet; +#[derive(Clone, ValueEnum)] +pub enum ItemType { + Variant, +} + pub fn init(repo: &Repository) -> Result<()> { let pre_commit_file_template = include_str!("templates/hooks/prepare-commit-msg"); let base_path = repo @@ -174,6 +180,21 @@ fn filter_missing_commits<'repo>( Ok(missing) } +pub fn list(repo: &Repository, kind: ItemType) -> Result<()> { + match kind { + ItemType::Variant => { + let variants = repo.references_glob("refs/variants/*")?; + for variant in variants { + if let Some(name) = variant?.name() { + let short_name = name.strip_prefix("refs/variants/").unwrap_or(name); + println!("{}", short_name); + } + } + } + } + Ok(()) +} + // TODO: building each time can be expensive. Look for an alternative fn extract_change_id(msg: &str) -> Option { let re = Regex::new(r"CHANGE_ID:\s*([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b)").unwrap(); diff --git a/src/main.rs b/src/main.rs index c65199e..21d30a2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use anyhow::{Context, Result}; use clap::{Parser, Subcommand}; use git2::Repository; -use git_varcs::{derive, diff, init}; +use git_varcs::{ItemType, derive, diff, init, list}; #[derive(Parser)] #[command(name = "git-vms", version)] @@ -31,6 +31,12 @@ enum Commands { #[arg(short, long)] reverse: bool, }, + /// List items in the repository + List { + #[arg(short, long)] + /// Type of the item to list + r#type: ItemType, + }, } fn main() -> Result<()> { @@ -50,6 +56,9 @@ fn main() -> Result<()> { target ))?; } + Commands::List { r#type } => { + list(&repo, r#type).context("Error listing objects")?; + } } Ok(()) }