Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Editing a commit’s diff with jj diffedit

jj split pulls one change into two, and jj squash moves work between changes. Sometimes we don’t want to move anything, though. We just want to fix what one commit contains: perhaps there’s a stray debug line three commits back, or a file that shouldn’t have been touched.

With git, we might start an interactive rebase, stop at the commit, amend it, and continue. jj diffedit gives us a more direct route. It opens the commit’s diff, lets us edit it, and writes the result back without checking out the commit.

Fixing a commit in place

Let’s point it at the revision we want to fix:

$ jj diffedit -r 'description(glob:"add feature*")'

With the default configuration, we’ll get the same TUI we saw with jj split and jj squash -i, showing that commit’s diff. The left side is the parent, and the right side is the commit. We can edit the right side until it contains what we intended, perhaps unticking a line we didn’t mean to add or deleting a debug statement. When we close the editor, jj updates the commit and rebases everything on top of it:

$ jj diffedit -r 'description(glob:"add feature*")'
Rebased 1 descendant commits.
Working copy  (@) now at: pxukppkx 5f724084 (empty) (no description set)
Parent commit (@-)      : nmvqtsqq f1b645ea add feature

Our working copy never moved! We didn’t need to check out the commit, use jj edit, or run jj new afterwards. As with any rewrite, rebasing its descendants can introduce conflicts that we’ll then need to resolve.

What you can and can’t do here

jj diffedit edits the content of one commit against its parent. You’re changing what the patch does, not where it sits. For the neighboring jobs:

  • Moving a whole file’s contents in from another revision is jj restore.
  • Moving a hunk between two commits is jj squash -i.
  • Splitting one commit into two is jj split.

That leaves jj diffedit for the case where one commit’s diff is almost right and we want to fix it in place.

Comparing two revisions instead

We can also use --from and --to to edit the diff between any two revisions. diffedit will write the result into the --to side:

$ jj diffedit --from A --to B

The -r form is just the common case of this, where --from is the parent. Most of the time -r is what you want.

Like every rewrite, editing a commit gives it a new commit ID and rebases its descendants, so the immutable rule applies: jj won’t let you diffedit a commit on trunk.