by stouset | 2025-08-14 20:43:00+00:00
In git you can also revert all the changes and you haven't modified anything important! `jj restore` or `jj abandon` would let you undo your changes or abandon the entire revision.

As others have noted, checking out a branch and accidentally modifying one of the commits on it is actually kind of hard to do. `jj git fetch` pulls in remote changes to your repo, but you don't "check out a branch": you either `jj edit` an existing commit or create a new one entirely with `jj new`. So you're unlikely to accidentally overwrite a commit by accident. I even find myself using `jj edit` less and less these days; if I want to change an existing commit (even if it's in the middle of a chain of commits), it's easy to `jj new <commit-to-modify>` and then `jj squash` the modifications into it. This allows me to double-check the modifications I'm making before I decide to commit to them (pun intended).

That said, I absolutely have forgotten to `jj new` before. Mostly this happens when I plop down at the laptop and start making changes without remembering to make a new revision. Whatever revision I happened to be on gets all those edits. Sometimes I work for quite awhile before realizing this, and so having to pick out which changes belong where piecemeal would be a ton of work.

But this is precisely the power of having all these changes in the repo as a revision. I can use all my SCM tooling to solve the problem for me! For the sake of this example, let's assume that I've pushed all my changes to a remote branch and then stupidly continued editing them. Now my local copy of branchname and branchname@origin have diverged; they both have the same revision, but different git contents.

# Oh no! I've accidentally made edits to `master`. `omny` points to two # separate git commits; one is my local copy of `master` and one is the # remote. > jj log @ omny me@example.com 2025-08-14 13:22:15 master* ae25 │ Made some changes │ ◆ omny hidden me@example.com 2022-03-26 14:49:58 master@origin 79d4 ╭─╯ Made some changes ◆ notn me@example.com 2022-03-23 17:03:05 b192 │ Earlier changes # First, rebase our local changes onto the remote commit. This neatly # splits out the changes I just made from the changes that were there # before I messed up. > jj rebase --branch master --destination master@origin Rebased 1 commits to destination Working copy (@) now at: omny?? 67a9 master* Parent commit (@-) : omny?? 79d4 master@origin # You can see that now my local changes have been rebased on top of # the remote copy. Unfortunately, they still have share the same # revision id so jj is not happy with me! We'll have to put those # changes into a new revision and make sure our local `master` points # to the same id as the one at `origin`. > jj log @ omny?? me@example.com 2025-08-14 13:28:14 master* 67a9 │ Made some changes ◆ omny?? me@example.com 2022-03-26 14:49:58 master@origin 79d4 │ Made some changes ~ # Make a new revision for our changes to go into. > jj new Working copy (@) now at: plwu 70d8 (empty) Parent commit (@-) : omny 79d4 master # Abandon the local revision where we accidentally changed the # `master` branch. Normally this would get rid of our changes, # but `--restore-descendants` makes sure that the filesystem # contents of the new revision we just made remain unchanged. # Since we're getting rid of the revision that made those edits, # those edits have to be moved up into that new revision in order # for it to look the same as it did before! # # Abandon would also normally delete our local copy of the # `master` bookmark. But `--retain-bookmarks` keeps it, and pushes # it back one revision. This is exactly what we need! > jj abandon master --restore-descendants --retain-bookmarks Abandoned 1 commits: omny?? 67a9 master* Rebased 1 descendant commits (while preserving their content) onto parents of abandoned commits Working copy (@) now at: plwu 69ce Parent commit (@-) : omny 79d4 master # Everything is happy again! My recent changes are now on their # very own revision. > jj log @ plwu me@example.com 2025-08-14 13:33:45 69ce │ (no description set) ◆ omny me@example.com 2022-03-26 14:49:58 master 79d4 │ Make some changes ~

I want to be clear about something here. I have never done this before. This isn't a pattern that's ingrained into me. I've certainly accidentally edited the wrong revision before, but it's always been relatively easy to split out the changes I made so I've never needed to really solve this in the general case before.

I read your comment, figured this would probably be the easiest way to do it using the primitives I'm familiar with, and I tried it. And it worked! I didn't need to go digging through manpages to figure out how to do it, it was a simple composition of commands I use every single day. I did this on a live, real repo I am actively working on where I had edits in flight, and just for giggles I squashed them into master. I had zero fear about doing this because a `jj op restore` would get me back to safety no matter what.

This will also work basically unmodified if you haven't pushed the changes. The only difference is you'd use the raw revision id instead of `master` and you'd use git commit ID of the earlier changes you wanted to keep (pulled from `jj evolog`), which you'd substitute everywhere I used `master@origin`. This works for every case where you have two sets of changes in one revision and you want to pull out the later changes from the earlier ones.