arxanas
Jujutsu actually doesn't take a strong stance here. I've personally implemented a centralized cloud-based backend for Jujutsu, while preserving the local commit manipulation workflows. It's a choice for the deploying organization to make.
Thanks for the link to the paper!
I wrote detailed commentary here: https://github.com/s-arash/ascent/discussions/72
Re Rust bindings and your specific comment:
- Deploying Soufflé and doing FFI is much more difficult for me in practice, just in terms of the overhead to set up a working build. (I'm not going to be able to justify setting up a Soufflé ruleset for Bazel, and then adding Rust-Soufflé binding generation, etc. at my workplace.)
- User-defined functors, or integrating normal data structures/functions/libraries into your Soufflé program, seems painful. If you're doing integrations with random existing systems, then reducing the friction here is essential. (In slide 16 of the talk, you can see how I embedded a constructive `Trace` type and a `GlobSet` into an actual Ascent value+lattice.)
- On the other hand, you might need Soufflé's component system for structuring larger programs whereas I might not (see above GitHub discussion).
Non-specifically:
- Several features like generative clauses, user-defined aggregations, lattices, etc. seem convenient in practice.
- I had worse performance with Soufflé than Ascent for my program for some query-planning reason that I couldn't figure out. I don't really know why; see https://github.com/souffle-lang/souffle/discussions/2557
- Recommend Ascent (Rust only, but supports targeting WASM)
- Soufflé: good, but too hard to integrate into existing systems; lots of ergonomic problems in comparison to Ascent (can elaborate)
- CozoDB: really cool but seems to be abandoned
- Logica: have not tried it yet
- That way, tests will be skipped when the contents of the commit are the same, while remaining insensitive to things like changes to the commit message or squashes.
- But they'll re-run in situations like reordering commits (for just the reordered range, and then the cache will work again for any unchanged commits after that). I think that's important because notes will follow the commits around as they're rewritten, even if the logical contents are now different due to reordering? Amending a commit or squashing two non-adjacent commits may also have unexpected behavior if it merges the notes from both sides and fails to invalidate the cache?
- This is how my `git test` command works https://github.com/arxanas/git-branchless/wiki/Command:-git-...
---
I've also seen use-cases might prefer to use/add other things to the cache key:
- The commit message: my most recent workflow involves embedding certain test commands in the message, so I actually do want to re-run the tests when the test commands change.
- The patch ID: if you specifically don't want to re-run tests when you rebase/merge with the main branch, or otherwise reorder commits.
Unfortunately, I don't have a good solution for those at present.
As a mental model, you can pretend that a commit with a conflict in it has suspended the subsequent rebase operation for descendant commits. When you resolve the conflicts in that commit, it's as if jj automatically resumed the rebase operation for the descendant commits. This process repeats until you've applied all of the rebased commits and resolved all of the conflicts (or not, if you decide to not resolve all of the conflicts).
Today, you can write something like
for commit in "$(jj log -T ...)"; do jj edit "$commit" && make fmt; done
which would also edit the commits automatically. It's probably not too hard to break out of the loop when the first conflict is detected to match the Git workflow more directly. (I can figure it out if you end up being interested.)> There's also no examples for how you work out which commit has a conflict marker. It's probably obvious in jj status but it would be nice to document, as I'm not yet interested in spending the time to test it.
Does this overview help? https://jj-vcs.github.io/jj/v0.26.0/tutorial/#conflicts
> And I imagine it all breaks in colocated repos too (I need submodule support, and I want to use gh cli etc)
It depends on the exact subset of features you need.
- jj can't update submodules, which works for some workflows but not others.
- Generally speaking, you can use the `gh` CLI reasonably well alongside jj. In jj, it's common to create commits not addressed by any branch, but `gh` doesn't support those well. You can either try to adopt your Git workflows directly and always use branches, or else you'll need to make sure to create the branch just-in-time before performing a `gh` invocation that would need a branch.
I think it's unfair to call it a "thin UI layer". My own project git-branchless https://github.com/arxanas/git-branchless might more legitimately be called a "thin UI layer", since it really is a wrapper around Git.
jj involves features like first-class conflicts, which are actually fairly hard to backport to Git in a useful way. But the presence of first-class conflicts also converts certain workflows from "untenable" to "usable".
Another comment also points out that it was originally a side-project, rather than a top-down Google mandate.
- run checks on the sequence of done commits, without getting wrong/unhelpful results because of the presence of an unfinished commit in the sequence
- put the done commits up for review now
(This is predicated on certain workflows being acceptable / useful for the user, such as modifying existing commits and reordering commits.)
It's also just an example. You can swap in your favorite query into `-r` if you don't have any need for moving `TODO` commits around. Regardless, Git is not easily able to replicate most such invocations.
I previously meant to link to the jj revset docs here: https://jj-vcs.github.io/jj/v0.26.0/revsets/
Off of the top of my head, `jj new` and occasionally `jj rebase` can create merge commits; I don't recall any others.
There's several things Git can't undo, such as if you delete a ref (in particular, for commits not observed by `HEAD`), or if you want to determine a global ordering between events from different reflogs: https://github.com/arxanas/git-branchless/wiki/Architecture#...
In contrast, jj snapshots the entire repo after each operation (including the assignment of refs to commits), so the above issues are naturally handled as part of the design. You can check the historical repo states with the operation log: https://jj-vcs.github.io/jj/latest/operation-log/ (That being said, there may be bugs in jj itself.)
jj rebase -r 'mine() & diff_contains("TODO")' -d 'heads(@::)'
in any reasonable number of commands, which will1) find all of the commits I authored with the string `TODO` in the diff
2) extract them from their locations in the commit graph
3) rebase all non-matching descendant commits onto their nearest unaffected ancestor commits
4) find the most recent work I'm doing on the current branch (topologically, the descendant commit of the current commit which has no descendants of its own, called a "head")
5) rebase all extracted commits onto that commit, while preserving the relative topological order and moving any branches.
Furthermore, any merge conflicts produced during the process are stored for later resolution; I don't have to resolve them until I'm ready to do so. (That kind of VCS workflow is not useful for some people, but it's incredibly useful for me.)
I believe that `jj squash` and `jj backout` also operate on edges rather than nodes, but the examples here don't make it clear. `jj squash` ought to combine the edge `r -> q` with the edge for `q -> parent(q)` (not depicted) and ultimately leave the `r -> q` edge as "empty", and `jj backout` ought to create an edge that has the inverse diff of another edge (which, in this case, is indistinguishable from `s`'s node changing to be equivalent to `q`'s node).
(To run the tests in parallel, git-branchless provisions its own worktrees. For binary search, it speculatively executes the search for the potential success and failure cases; when the number of jobs is a power of 2, this partitions the search space evenly.)
[1]: https://github.com/arxanas/git-branchless/wiki/Command:-git-...
These days, there are relatively few things that git-branchless can do that jj/Sapling can't, one example being its (somewhat opinionated) `git test` command: https://github.com/arxanas/git-branchless/wiki/Command:-git-...
- https://blog.waleedkhan.name/git-undo/ — has a link to this article explaining some things that can't be undone in the Git model via reflog: https://github.com/arxanas/git-branchless/wiki/Architecture#...
- https://blog.waleedkhan.name/in-memory-rebases/ — It's non-trivial to support workflows like "rebase just commit X out of its branch and insert it before commit Y in that branch" in a single command. jj implements the same workflows as well.
> [...] [C-Reduce] produces outputs that are, on average, more than 25 times smaller than those produced by our other reducers or by the existing reducer that is most commonly used by compiler developers. We conclude that effective program reduction requires more than straightforward delta debugging.
(Of course, this means that C-Reduce is 12 years old now.)
At the same time, C-Reduce seems to be more general than the LLVM tool you linked ("BugPoint", dating to 2002), since that one works with LLVM IR specifically.
I think most developers are generally unfamiliar with automatic test case minimization tools and techniques, so the post may be helpful even if the ideas have been known in their respective circles for quite some time.
To give some specific examples, "many people" includes popular open-source projects like Linux and Git itself, as well as large tech companies like Google and Meta, which employ "trunk-based development" (see e.g. https://trunkbaseddevelopment.com).
- Currently no partial/shallow clones, so you need to materialize the entire repo on disk.
- Working copy status can take a while (see https://github.com/martinvonz/jj/issues/1841 for tracking issue). This can be ameliorated at present by configuring Watchman as a filesystem monitor, and work is underway to improve status further.
- No support for Git LFS at present (even in colocated repos). When using the Git backend with jj, you would expect the same problems with regards to large file management.
- I haven't noticed any particular performance issues when interacting with the object store a large repository. It should be approximately the same as for Git since it uses libgit2 for the Git backend.
- Faster for operations that can be done in-memory in jj but only on-disk with Git, such as various rebase operations.
From the README, no advantage for now.