Skip to content

Instantly share code, notes, and snippets.

@elithrar
Last active January 10, 2026 02:45
Show Gist options
  • Select an option

  • Save elithrar/4a09ef750af5624b729a6f1d87a0431c to your computer and use it in GitHub Desktop.

Select an option

Save elithrar/4a09ef750af5624b729a6f1d87a0431c to your computer and use it in GitHub Desktop.
a jj cheat sheet for git users

jj Cheat Sheet for Git Users

🗣️ this is a useful guide to help you memorize + better understand how Jujutsu works, but it's not a replacement for learning the mental model or deeper strategies!

Core Concepts

  • No staging area — all changes in the working copy are automatically part of the current “change”
  • Changes vs commits — jj tracks “changes” (mutable) that become “commits” (immutable) when pushed
  • @ — a symbol pointing to the current working copy change (like HEAD, but for changes)
  • @- — the parent of @
  • Change ID — stable identifier that survives amends; commit ID changes on every edit

Quick Reference

Git jj
git pull jj git fetch + jj rebase -d main@origin
git commit -a jj commit
git commit --amend jj describe -m "msg" or jj squash
git stash jj new (WIP stays in @-)
git checkout -b jj new -B <name>
git add -p (automatic — use jj split to separate)
git reset --soft HEAD~1 jj edit @- (modify parent directly)
git reset --hard HEAD~1 jj abandon

Common Workflows

Commit and push to main

jj describe -m "feat: my change"
jj bookmark set main -r @
jj git push

Create a branch, work, push

jj new main -B new-feature        # new change + bookmark
# work, jj describe as you go
jj git push -b new-feature        # push to remote

To push more changes later:

jj new
jj describe -m "more work"
jj bookmark set new-feature -r @  # move bookmark forward
jj git push -b new-feature

Move existing changes to a new branch

jj bookmark create new-feature -r @   # bookmark current change
jj git push -b new-feature            # push it
# keep working, jj bookmark set to advance

Merge branch back into main

jj new main new-feature    # create merge commit with both parents
jj bookmark set main -r @  # advance main to the merge
jj git push -b main

Or rebase (linear history):

jj rebase -r new-feature -d main   # rebase onto main
jj bookmark set main -r new-feature
jj git push -b main

Pulling

Git jj
git fetch jj git fetch
git pull jj git fetch then jj rebase -d main@origin
git pull --rebase Same as above — jj rebases by default

Pushing

Git jj
git push jj git push
git push -u origin branch jj git push -b branch
git push --force jj git push (force is automatic for rewritten changes)

Note: You need a bookmark (jj’s term for branch) to push. Create one with jj bookmark create <name>.


Amending

Git jj
git commit --amend jj squash (squashes @ into @-)
git commit --amend -m "new msg" jj describe -m "new msg" on the target change
git add -p && git commit --amend Just edit files — they’re auto-included in @, then jj squash

You can also amend any change directly:

jj edit <change-id>   # switch to that change
# make edits
jj new                # create a new change on top when done

Branching (Bookmarks)

jj calls branches “bookmarks” — they’re just pointers to changes.

Git jj
git branch <name> jj bookmark create <name>
git checkout -b <name> jj new -B <name> (create change + bookmark)
git branch -d <name> jj bookmark delete <name>
git branch -m old new jj bookmark rename old new
git checkout <branch> jj new <bookmark>

List bookmarks: jj bookmark list

Key difference: Bookmarks don’t auto-advance. Use jj bookmark set <name> -r @ to move them forward.


Remotes & Forks

Adding remotes

Git jj
git remote add origin <url> jj git remote add origin <url>
git remote add upstream <url> jj git remote add upstream <url>
git remote -v jj git remote list

Pushing to a fork

jj git push --remote fork -b my-feature

Fetching from upstream

jj git fetch --remote upstream
jj rebase -d main@upstream

Tracking remote branches

jj bookmark track main@upstream

Workspaces (Worktrees)

jj calls worktrees “workspaces”.

Git jj
git worktree add ../path branch jj workspace add ../path
git worktree list jj workspace list
git worktree remove ../path jj workspace forget (from that workspace)

Switch which change a workspace is editing:

jj workspace update-stale   # sync after changes in another workspace

Stashing

jj doesn’t need stashing — the working copy is always a change. But equivalent workflows:

Git jj
git stash jj new (your WIP stays in @-)
git stash pop jj squash --from <wip-change>
git stash list Just use jj log — nothing special about “stashed” changes

To temporarily set aside work:

jj new           # current work becomes @-, you get empty @
jj edit @-       # go back to it later

Squashing & Rewriting History

Unlike git rebase -i, jj doesn’t have a single interactive command. Each operation is atomic:

Task jj
Squash into parent jj squash (squashes @ into @-)
Squash specific change jj squash --from xyz (into its parent)
Squash into arbitrary target jj squash --from xyz --into abc
Split a change jj split (interactive)
Reorder changes jj rebase -r <change> -d <new-parent>
Edit old change in-place jj edit <change>, make edits, jj new
Reword message jj describe -m "new message"
Abandon (delete) a change jj abandon <change>

Git reset equivalents

Git jj What it does
git reset --soft HEAD~1 jj edit @- Go back to parent, modify it directly
git reset --hard HEAD~1 jj abandon Throw away the change entirely
git reset --mixed HEAD~1 (no equiv) jj has no staging area

Bisecting

Git jj
git bisect start jj bisect start
git bisect good <ref> jj bisect good or jj bisect good <change>
git bisect bad <ref> jj bisect bad or jj bisect bad <change>
git bisect reset jj bisect reset
git bisect skip jj bisect skip

Typical flow:

jj bisect start
jj bisect bad @      # current is broken
jj bisect good xyz   # xyz was working
# jj checks out the midpoint, test, then:
jj bisect good       # or bad
# repeat until found
jj bisect reset

Logs & Search

Task Command
See history jj log
Last N ancestors jj log -r 'ancestors(@, 10)'
All bookmarked changes jj log -r 'bookmarks()'
Status jj status or jj st
Diff working copy jj diff
Diff specific change jj diff -r <change>
Show a change jj show <change>
Undo last operation jj undo
Operation log jj op log

jj commit vs jj describe

  • jj commit = jj describe + jj new — set message and start next change
  • jj describe — set/update message, keep working in same change

Use jj describe while working, jj commit when you’re done and moving on.


Shell Completions

# Add to .zshrc
source <(jj util completion zsh)

Or for oh-my-zsh, generate to a file:

jj util completion zsh > ~/.oh-my-zsh/completions/_jj
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment