So I rewrote StGit in Rust #185
Replies: 4 comments 9 replies
-
Nice! One of my main motivations to write the first version of StGit was to learn Python, so I can see the appeal to re-write it in Rust (maybe I'll start contributing again for similar reasons ;)). I haven't looked at the code-base yet and I'm pretty novice in Rust. On one hand, we may get more contributors who are excited about Rust and want to join a fun project. On the other hand, there may not be so many knowledgeable people around compared to Python (and Rust is substantially more difficult, just try to do a doubly-linked list ;)). Another downside is the dependencies, Rust doesn't come with "batteries include" like Python. Distro packaging may become problematic. Anyway, I think that's great. If we keep 1.x in a stable state, we can always revert to the Python version but I don't think we'd need to. W.r.t. the commands left, I think 'copyright' needs some change as I'm not really the owner. I even forgot this command existed and I don't remember why I added it. A file in the top source directory should suffice. Personally I'd keep it as GPLv2 and maybe add "SPDX-License-Identifier: GPL-2.0-only" lines in all source files. What I can't tell is how compatible the GPL is with the Rust dependencies. Even changing the StGit license from GPL to something else is slightly problematic since there are lots of contributors we'd need to chase and get them to agree. That's one of the reasons I'd stick with GPLv2. |
Beta Was this translation helpful? Give feedback.
-
I did want to try this but it seems nearly impossible on current Debian stable. The cargo version complains about 'edition' being 2021 when supports up to 2018. Changing 'edition' to 2018 goes into other issues and some error about unstable features. As I'm only barely familiar with Rust, I can't tell whether that's fixable, so I didn't log a bug against it (I'll compile my own rustc/cargo eventually but it would be nice to support Debian out of the box; maybe by the time 2.0 gets released, Debian will update to whatever cargo version is in testing). |
Beta Was this translation helpful? Give feedback.
-
RIIR might be a trope in some circles, but StGit is the first time that a tool I'm actively using has migrated to Rust and I need to upgrade to keep it up-to-date. Now I'm more motivated to contribute fixes to StGit, as I'll be learning Rust in addition to improving this great tool. Thank you, @jpgrayson! |
Beta Was this translation helpful? Give feedback.
-
Any idea how to get this to build on Fedora as an RPM? I've used rust2rpm to generate a specfile, but it doesn't put in the BuildRequires for dnf builddeps to use - and even if I put in the deps manually, Fedora doesn't carry all of them. Further, I tried to used cargo to install the "git-repository" create (since this isn't in Fedora), but it fails because there's no executable in there. |
Beta Was this translation helpful? Give feedback.
-
The title says it. Over the past several months I have rewritten StGit in the Rust programming language. It's been great fun, and, I believe, successful.
Status Summary
The Rust implementation exists and works. And it is fast. It is ready for the StGit community to take it for a test drive.
Of course there remains work to do before it is release-ready.
spill
(replacesstg refresh --spill
)copyright
: disposition: remove and add copyright notice tostg version
clone
mail
(The future of `stg mail` #186)stg top
, which one might want to include one's shell prompt, improved from about 80ms to about 20ms.--color=never
command line option.new
,edit
,squash
,refresh
, andimport
.build.rs
script that does docs and completion businessMotivations
Given that "RIIR" has become a bit of a trope, I feel compelled to lay out the motivations. So here is why I rewrote StGit in Rust:
stg series
taking several hundred milliseconds, I may just be a latency snob. But as a StGit maintainer, the amount of time it takes to run StGit's test suite directly affects my ability (and will) to work on StGit.Implementation Notes
Git vs. libgit2 vs. gitoxide
The Python implementation of StGit runs subordinate
git
processes for all operations on the Git repo.For the Rust implementation, I landed on an
unholyhybrid approach using both thegit
executable and git2-rs. The git2 (library) turned out to be good for accessing Git data structures such as the object database, config, references, and index. Where git2 was not as strong was with performing some operations that modify the underlying repository. A key example is patch application wheregit2::Repository::apply_to_tree()
can have different (wrong) outcomes compared togit apply
. Additionally, there are numerous higher-levelgit
command behaviors that StGit relies on that would be difficult to replicate using the lower-level git2 capabilities (thinkgit pull
orgit commit --gpg-sign
).So the general delineation is that
git
is used for non-trivial modifications to the repository and git2 is used for the simple, low-level (and high-frequency) accesses to repository state.N.B. I did explore using subordinate
git
processes for everything, i.e. no libgit2, like how the Python implementation works. I was really hoping that this approach could work since it would cleave-off a significant portion of the dependency tree. I went so far as to implement a relatively completestupid2
library with agit2
-like interface, but usinggit
subprocesses under the hood. The result was: a lot more code and much less performance. The performance landed somewhere between the current Rust implementation and the Python implementation.On gitoxide
I also evaluated using gitoxide which is an ambitious ground-up implementation of git in Rust. The bottom line is that gitoxide does not yet provide sufficient API's to replace
git2-rs
. It will be interesting to reevaluate gitoxide for StGit when gitoxide reaches sufficient feature parity with git2.Third-party dependencies
One thing about the Rust implementation of StGit that doesn't compare well to the Python implementation is its reliance on third-party dependencies. Whereas the Python implementation has zero runtime dependencies on third-party Python packages, the Rust implementation has 19 direct dependencies and 64 direct+transient dependencies.
A key downside to having all these dependencies is increased exposure to supply-chain attacks. This is a problem faced by the whole ecosystem. For StGit, the approach will be to limit direct and transient dependencies to those that are necessary and well established. I'll also be keeping an eye on what the broader Rust community is doing in this space as well as looking to downstream packagers/distros for best practices.
Safety
Type Safety
Expressing StGit's data structures and behaviors using Rust's robust type system has been a joy. There were plenty of challenging bits and pieces, which helped push me up the Rust learning curve, but in the end it felt tremendously productive to have so much more correctness baked-in at compile time versus what may only be uncovered at runtime when implementing in Python.
One typing success story is
struct PatchName(String)
, a tuple-style struct wrapping a regular RustString
, that maintains the invariant of only allowing strings that follow all the weird patch name rules.PatchName
is used prolifically throughout StGit and everywhere we see aPatchName
we can have 100% confidence that we're dealing with a valid patch name. Contrast to the Python implementation which usesstr
(without type annotations) when dealing with patch names such that encountering a variable namedpatch
, you first have to wonder whether it is astr
or some other type (orNone
), and if it is astr
, whether it has been validated to meet the patch naming rules. Barbarous!Another success (I think) are stack transactions which are first setup using the builder pattern, then passed a closure which serves as a sandbox for the transaction operations to be defined, before finally being executed, consuming an old
Stack
and producing a newStack
. The boundaries imposed bystruct StackTransaction
and friends avoids mutable state and makes it easy to setup and perform a stack transaction correctly.Memory Safety
StGit only uses
unsafe {}
in one place in thestupid
(git
wrapper) module to avoid potentially costly clones and can be replaced once scoped threads is stabilized.StGit's biggest exposure to memory-unsafe code is via
git2-rs
, which wrapslibgit2
, which is implemented in C.Get Involved
It would be great if some StGit veterans would give the Rust version a try.
File bugs at https://github.com/stacked-git/stgit/issues/new?labels=rust
Otherwise, I would love to hear your thoughts on the rust implementation and/or StGit 2.0 here in this GitHub discussion.
Thanks!
Pete
Beta Was this translation helpful? Give feedback.
All reactions