defeating git rigour fatigue with jujutsu

🔥 Check out this awesome post from Hacker News 📖

📂 **Category**:

✅ **What You’ll Learn**:

This post assumes a basic level of familiarity with the
jujutsu version control system. If you haven’t used jujutsu, you’ll still get the gist of the idea, but I
recommend reading
Steve’s Jujutsu tutorial
after.

When developing a large feature, writing Good Commits is hard.

And by Good Commits, I mean something like:

define types
add DB functions
server CRUD
client API
client UI

This allows reviewers to step through your pull request in small bites, with
each set of changes scoped to a single aspect of the feature.

So, naturally, here’s what I do instead:

define types
add DB functions
WIP test code
server CRUD
client API and UI
fix DB function
fix UI bug
refactor CRUD
fix another UI bug

Latter commits overwrite work that was done in earlier commits and the story
breaks.⚖️

Jujutsu makes it easier to hop around commits and iterate quickly on
compartmentalized changesets, but it’s still effortful and I get averse.🤖

jj absorb
helps somewhat, as does
jj squash -i, but they both have their downsides:

  1. absorb assigns the changes based on whichever
    previous commit most recently touched those files, which sometimes doesn’t
    actually correspond to which commit should own these particular
    changes.
  2. squash can get you stuck in merge conflict hell
    if your boundaries aren’t extremely clean.

So here’s a solution to this problem of “git rigour fatigue” that I’ve come
up with.

For this example, let’s represent commits visually. Imagine
red represents
changes to the type definitions,
blue to the UI
and so on:

Mayhem. Our first commit is a mix of
red and
blue. We touch
red in multiple
places!

To fix this, let’s create our ideal commit history first, using
jj new -B messy-first -m ‘red’

Then we can do the rest. (I switch to
jj new -A red -m ‘blue’ at this point)

Then we squash all the commits with actual changes in them into one with
jj squash –from messy-first..messy-last –into messy-first

Then we use
jj squash -i –from messy-first –into red
and pick out the red changes, putting them into the red box:

And so on:

Eventually everything’s in the right place and the “everything commit” is
empty.

For large features, I find this workflow far easier than having to maintain
strict git rigour for the lifecycle of the feature’s development. It’s
easier to make improvised commits with temp debugging state in them and tidy
it all up in one sweep at the end.

preemptions:

  1. I don’t have a good name for this technique. “Doing Commits Like A Big
    Pile Of Laundry”, perhaps?
  2. This is different from (and, imo, superior to)
    jj split:

    1. With split, if I miss a hunk that should have
      been in red,
      I have to split again and squash.
    2. This technique more easily allows sorting the easiest hunks at the
      beginning without worrying about how it will effect the commit
      sequencing.
  3. This reason why doing it all at the end is (often) better than using
    jj squash -i as you go is because the final state
    of the everything commit is guaranteed to not have any conflicts. Creating
    a new “fix red and green” commit and interactively squashing that into
    your red and green commits might break your blue commit if it happens to
    touch one of the affected files.
  4. A downside to this technique is that there’s no guarantee that every
    commit will compile, which might be a dealbreaker.

⚖️
Some people prefer this, as it helps
git bisect work better. Debuggability versus
reviewer convenience is the tradeoff, I guess.

🤖
Especially in a world of LLM agents, that will gladly fix bugs for you
that span multiple boundaries in 30 seconds.

🔥 **What’s your take?**
Share your thoughts in the comments below!

#️⃣ **#defeating #git #rigour #fatigue #jujutsu**

🕒 **Posted on**: 1779662835

🌟 **Want more?** Click here for more info! 🌟

By

Leave a Reply

Your email address will not be published. Required fields are marked *