Last time I tried jj, branches were an extremely laborious process to keep up to date. I don't see how people that aren't working alone can work with that. I have numerous branches in flight at any given time, and my colleagues do as well. The idea of manually keeping them pointed at the right commit is just nuts.
Maybe they've fixed that astonishing choice since then, and I'd give things another go if they did. But branches and worktrees are how I operate.
Regarding the article, I have no idea what is going on as I'm red-green color deficient.
There are just multiple different ways of working. Some ways fit some people's mental models better.
You're not going to get a definitive "jujutsu is better than git" or vice versa. You should accept that some people have no problems with what you've described using jujutsu, and likewise jujutsu users should understand that not everyone can handle jj as well as they can.
Imagine a different thread where jj users take your exact scenario, and complain about solving the problem with git. You wouldn't understand their pain, because it's not painful for you. This thread is the same, just with jj and git reversed.
Personally, I don't see the pain you have. Back when I used git a lot, if I left a branch for a few weeks, I'd forget the name of the branch and would have to list all the branches (and set an alias to sort by and list the last commit dates of each) to discover the appropriate branch name. It's really not all that different from looking at all (recent) heads. Once you get used to this, you stop naming branches - other than to share with others. And when you do share with them, you cannot push (newer) changes because only bookmarked nodes and their parents can be pushed - so just prior to pushing, you advance the bookmark. With the shells I use, it's a few keystrokes before autocomplete/fzf produces the command for me - no mental effort at all.
You definitely wouldn't advance the bookmark with each commit. Only when you need to push.
And oh man, it's so nice not to have to manage all the branches. With git, I'd routinely go and delete old branches to declutter. With jj, there's simply no need to. The same with stashes. It's really nice not having to do that labor, and simultaneously not dealing with long lists.
If this doesn't appeal to you - that's fine. You're not deficient. But understand, nor are those for whom your workflow sucks.
For example, your "not all that different from looking at all (recent) heads" implies that the number of (recent) heads isn't far off from number of (would-be-)branches (i.e. no random offshoot experiments, stashed-away debug sessions; whereas I make many of such continuously (were stashes on git (with occasional grumbles about not being able to stack stashes), regular commits now on jj (maybe with a special-format description, if I bother)));
and that you (even if subconsciously) try to ensure that the head of a branch is always identifiably-representative of the branch (i.e. don't put some random unrelated change at the tip with the idea of "I'll put this in a more proper place when I get back to this").
Effectively, using the full commit graph not as a place where anything potentially-useful can stay, but rather by itself a complete picture, with things not fitting into it going into.. idk, just being abandoned, to be found by looking through the op log? commit IDs saved in an external file? wading through evolog / scanning through `jj show -r xyz/2` etc?
If it is like you say and different people are just inherently more or less suited to different paradigms, then not everyone can be happy.
wait what? how does this work?
Many of my stashes are tiny changes that need checked later - effectively, each stash entry is a quick TODO list. At some moment, for example once a major feature is done, I'll go a check every stash'ed entry and decide - maybe it's no longer relevant, maybe I should make a PR out of it, or maybe I should convert them to the branch if it's useful but needs more work. The branches are similar, but on longer scale.
The idea that you don't need to declutter your old stashes/branches seems absurd to me - it's like getting a huge box labeled "misc", and throwing every single thing in there. Sure, it's quick, but that's how you lose the important things you need to do, and find the useless junk instead of the actual thing you were looking for.
Bookmarks aren’t that bad either IMO, especially with the recent addition of `jj bookmark advance`. Curious if you can say more about the particular difficulties you found keeping them up to date?
I often work on something and then switch away to something else. it might be a week before i get back to it, and the name of the branch is a clue as to what the heck I was doing.
Other people often need to check out a branch I'm working on to help. How does anonymous branching help anyone except a solo developer?
Is it easy?
I like Jujutsu so much that I've been working on massive refactors to my tooling in order to support it (example: https://github.com/LoganDark/get-shit-done)
But I don't rewrite history. It's history. While I can understand people have reasons to do it, the reasons have never resonated with me. I'd rather spend my time getting new work done and not polishing work I've already done.
I dislike this as well. I find it easier to keep track of branches with bookmarks, but my workflow still makes things cumbersome. I am usually working with the "megamerge" branches, and I usually want to add commits to my current branch instead of squashing my edits. However, adding commits means I have to add my commit, move my bookmark up to the branch tip (jj tug?), and then rebase the megamerge branch, versus doing nothing for squashing. I also find that when I mess up, I don't really love using `jj op log` to fix it. I want to not be in an environment where it's this easy to destroy history (I feel like git was on the other end of it).
That’s why I always use jj’s automatic commit identifiers. They are short and I don’t waste brain cycles naming things that are ephemeral. When I push, I let jj automatically creates, updates, and deletes remote git branches (`jj git push -c` for creation, plain `jj git push` for updates, `jj git push --deleted` for deletions). I do not ever have to think about branch names and it is great!
But when I'm picking up something someone on the team has left behind because they got pulled on to something else, or are sick, or 5 million other reasons, having a branch, with a ticket in the name, explaining what the purpose of the branch is, why it exists, what it's current state is, that all matters. I can't help but think that everyone that likes JJ isn't really doing collaboration.
Restricted and summarized is good - easier to find/remember, less fluff in a list. And easier to recognize a short identifier from a list of the 2-3 most recent branches, than scanning through 50 commits, when trying to remember where some work last was, and which is the proper end-point instead of some failed attempt or unrelated change.
Unnamed branches are quite neat - I certainly have a lot more of such than named ones in jj - but as such named branches are, if anything, more important as a result, for separating sequences of changes striving towards a goal, from the sea of smaller experiments.
branch names are just a summarization of your commit messages
What kind of dev workflow leads to this surprising opinion?You can't possibly see a use-case for long-lived branches? Say what you will of git, at least it exposes enough knobs that it can mostly accomodate every workflow (possibly with a heavy porcelain layer to hide the plumbing). JJ seems to swing too far in the other direction, great for a "live on head" mentality but less ideal for other setups.
(The fact that all edits are automatically recorded is my personal peeve with JJ. I'm ok with lack of staging area like in Mercurial, but mercurial doesn't try to automatically amend the commit with my pending changes. Sure I can pretend that "@" is my staging commit then squash as needed, but then I also need to remember that checking out a commit is "jj new" which feels like absurd mental gymnastics to me.)
I think the idea is that branches/bookmarks aren't as necessary as they are with git.
for prs, I usually start with a single commit, so `jj git push -c` will auto create a named branch based on the change id. And i have template like the following to push to the same branch if i decided to stack commits rather than rewrite:
branch-push = ["util", "exec", "--", "sh", "-c", """
name="$(jj log --no-graph --no-pager --color=never -r 'fork_point(@ | trunk())+ & fork_point(@ | trunk())..@' -T '"push-" ++ change_id.short()')"
jj bookmark set -r @- "${name}"
jj git push -b "${name}"
"""]
you could probably write a similar alias that used your workspace name as the branch name to push to.and descriptions are slightly nicer than branch names, since they can be longer.
Review was done by having someone sit next to you and scrolling through the code telling what you had changed.
The un-learning curve is steep if you already know git very well.
https://github.com/jj-vcs/jj/discussions/3549 lets you enable automatically advancing bookmarks.
It feels like Apple vs Linux. Apple being different ... just because (it gives them an artificial moat)
A checkout is a working space after all, why can't it be (temporarily) dirty whilst you work?
From there, the simplest way is to just always use 'jj commit -i' and 'jj squash -i' to create change ids with your work. Then if you want to have your changes move around with you, just rebase your working copy which contains your "uncommitted files" to the new branch.
A different idea is to put those changes in a separate change, and then when you do work, always create your working space change as a merge change like 'jj new <uncommitted file change id> <main change id>. Then you should be able to do 'jj absorb' instead of 'jj squash' to put changes into the right change. Switching to a different branch is 'jj new <uncommitted file change id> <other change id>.
As in typing this, I'm thinking for myself and what I actually do in practice... I find moving / rebasing jj commits very easy (I have a UI tool that literally lets you drag and drop them) so I usually just commit these changes and then drag the commits around so it's not in the chain of when I want to send it out for review
> why can't it be (temporarily) dirty whilst you work?
Because that would go completely against how jj changes work.
instead of adding changes to a new commit, i split/squash them into the previous one so the current commit remains dirty
git rebase -i
# squash all the commits (e.g. in vim with ctrl-v)
git reset HEAD^
git add -p
# interactively pickup the RED hunks
git ci -m RED
The main difference to jj is that the RED commit is created later with git.Jj is not git and is not a git tool, it just (thankfully) uses git as a backend, so you can still carry on with the rest of the world.
In what way is that different from using `git rebase -i` to edit a commit?
> Some people prefer this, as it helps git bisect work better. Debuggability versus reviewer convenience is the tradeoff, I guess.
Ideally we would have a VCS that made ergonomic to store both history-as-it-happened for some purposes, and the cleaned up, squashed and rebased history for other purposes, ensuring they match
You can revisit the original PR to see the individual commits if you really want.
Is that a frequent way of reviewing? On GitHub you get shown all changes together in the review tab. You can select individual commits for closer inspection, but where is the benefit?
I started doing exactly this and it's been invaluable.
For me it's satire. There are reasons for varying effort in creating PRs or patches, but attempts like this never seem to reason about reality. If I have to review, I want to see the code, not a clever story hidden in the commit history.
I just don't see the fuss about rebase. Merging just works fine.
Edit: OK, I realized later that I'm not really responding to the usual git rebase -i use case.
Have you heard people say that because of magit they started using more "advanced" git workflows, and how they emphasize having a better UI makes a difference?
It's the same idea with jujutsu. I'm much more likely to use git's power via jujutsu than directly with git. It's because jujutsu lets you do it all with a much simpler interface - fewer commands, and fewer concepts. And knowing that "jj undo" has your back.
As a sibling commenter said: Likely 99% of git users don't do "git rebase -i". But the percentage who do similar with jujutsu is high - perhaps over half[1] of jujutsu users do the equivalent of "git rebase -i" all the time. Many of them didn't when they used git.
The interface matters.
[1] If you told me 80%, it wouldn't surprise me in the least.
It tries to solve a human problem in an LLM era.
LLMs are destined to overcome humans in code merging and change versioning (already did for me).
There's little point to introducing yet another layer of indirection when LLMs just cut to the chase.
I’m pretty sure `jj absorb` (and its predecessors, `git-absorb` [0] and `hg absorb`) are smarter than this, instead looking at the actual diffs.
> [absorb] splits changes in the source revision and moves each change to the closest mutable ancestor where the corresponding lines were modified last. If the destination revision cannot be determined unambiguously, the change will be left in the source revision.
I use absorb fairly often, fwiw. It's great for when I want to make a patch to a commit that will easily absorb into its right place. And I also, sometimes, prefer the more intentional approach where I decide exactly where each hunk will go.
Basically, if you don’t get into that sort of situation with commits containing parts they shouldn’t in the first place, you don’t need to do any extra work to clean them up. The tip of your branch should be the only “messy” part.
I start a new branch and begin work. When I’m ready to start organizing that work into a consistent narrative (or when bits are “finished”), I split it out into independent commits. As I keep making fixes and tweaks, I continue squashing bits from my working commit into the parent commits they belong on.
I don’t bother making any independent commits until pieces of what I’m working on are becoming fully-formed. Until then, my working commit just has everything.
`git branch` is basically my bookmark tool. I commit for a while, then when I want to remember where I am for later, `git branch wip/topic-a-finally-compiles` or whatever. I can reset hard to it when I want to revert back, or any other topic I need. If I forget to name a branch for a commit, the reflog is right there. Nothing’s lost.
And yeah, a soft reset is basically the ideal way to just say “pretend all my changes weren’t committed yet, starting from $ref” and then I make my single commit for my PR. Easy peasy.
I do switch branch for long experiments and touch up on existing PR.
It would be great if a PR was about distributing patches and not having those automatically generated from a branch.
Seems straightforward, wouldn’t call it special
Yes, you can do most commit manipulations with git just like with jj. But, users of jj know they're "looking down the power continuum" (to reuse pg's terminology) when they look at git, whereas git users cannot fathom what's exactly the deal with jj. Unfortunately, the only way to get it is to spend a week with it, with an open mind. It's close to impossible to describe it except "it's really neat" and "wow it removes all git's friction I didn't know existed".
And, apparently, there's a pattern of having to try at least two times before it becomes intuitive!
I see there's a similar project for JJ, but it doesn't seem nearly as polished https://github.com/Cretezy/lazyjj
Just give me the PR, don't sweat the individual patches. But maybe also work on not committing your first idea as finished work.
The commits might get squashed anyways so the history on main won't necessarily match what's on the feature branch.
You can commit before you raise a pull request, I don't quite understand that point but I might just be missing something about your workflow that's different to mine.
Commits on a PR branch are usually my thinking outline, not for someone else to step through.
1. Squash all your commits in this branch to one
2. Move that commit to the working directory with the appropriate git reset command
3. Commit hunks as appropriate.
I don't know about all that. All sorts of ex-post-facto automated cut-up-and-splice commits sounds to me like a recipe for an every larger mess. I say maintain git rigor, always. Now, you could say "You only say that because you know git rather than jujutsu" or "if you use git absorb more you'll get it", and theoretically you might be right, but... meh, I kind of doubt it.
sorry about that.
To some of us, that's an essential structural criterion. Passing unit-level self-tests may be as well.
I'm not a git expert by any means I don't think, but there's 15 years of muscle memory there, and a few years of subversion before that...
jj let me make changes to my git commits without fear, since version control of the git state itself let me undo those changes easily, too.
It is also true in reverse. Scopes set too broadly ("dark mode implementation," "auth flow fixes") lead to un-readable diffs no matter what tool you use for version control. Un-readable diff does not stem from commitment discipline; it is a scope problem.
That said, this fact does not diminish the usefulness of Jujutsu. There are valid use cases for the rebase and stacking operations. However, the discussion about commit granularity takes on a whole new context once the constraint of having readable commits is established at the scope setting stage.