| name | jj |
| description | Use when working with version control. jj (Jujutsu) is the preferred VCS - never use git commands directly when jj can do the job. Covers mental model, workflows, and Nix integration. |
Jujutsu (jj) Version Control
CRITICAL: Always use jj instead of git. Never run git commands directly when a jj command will do.
Mental Model (Key Differences from Git)
Understanding these concepts prevents most jj mistakes:
1. Working Copy IS a Commit
- In jj, your working directory is always a commit (the "working copy commit")
- There's no concept of "uncommitted changes" - edits are automatically part of the working copy commit
- Changes are snapshotted automatically when you run jj commands
2. No Staging Area
- Everything is auto-tracked - no
git add needed
- Files are either tracked or ignored (via
.gitignore)
- To commit partial changes, use
jj split
3. jj new Creates Empty Commits
jj new creates a new empty commit ON TOP of the current one
- Your previous work stays in the parent commit
- Think of it as "I'm done with this commit, start fresh work"
4. Bookmarks, Not Branches
- jj uses "bookmarks" which are just pointers to commits
- There's no "current branch" concept
- You work on commits directly, bookmarks are optional labels
5. Automatic Rebasing
- When you edit a commit, all descendants are automatically rebased
- No manual
git rebase needed for most cases
Nix Integration (Critical Gotcha)
Problem: Nix flakes only see files tracked in git HEAD. New files are invisible to Nix until snapshotted.
Symptoms:
nix build fails with "file not found" for newly created files
nix flake check doesn't see your changes
- Module imports fail for new
.nix files
Solution: Snapshot working copy before Nix commands:
jj new
jj commit -m "wip"
nix build
nix flake check
Why this works: jj new or jj commit snapshots the working copy, which updates git HEAD (jj's colocated git repo), making files visible to Nix.
Command Mappings
| Task | Git | jj |
|---|
| Start work | git checkout -b feat | jj new -m "feat" |
| Stage + commit | git add . && git commit | jj commit -m "msg" |
| Amend | git commit --amend | Just edit files (auto-amended) or jj describe |
| Partial commit | git add -p | jj split |
| Interactive rebase | git rebase -i | jj rebase (auto-rebases descendants) |
| Undo | git reset/reflog | jj undo or jj op restore |
| Status | git status | jj st |
| Log | git log | jj log |
| Diff | git diff | jj diff |
| Create branch | git branch foo | jj bookmark create foo |
| Switch branch | git checkout foo | jj edit foo (or jj new foo) |
| Push | git push | jj git push |
| Pull | git pull | jj git fetch && jj rebase -d main |
| Merge | git merge | jj new commit1 commit2 (creates merge commit) |
| Stash | git stash | Not needed - just jj new and come back |
Key Workflows
Starting Fresh Work
jj new main -m "feat: add user authentication"
Editing an Old Commit
jj log
jj edit xyz
jj new
Squashing Commits
jj squash
jj squash --into @-
jj squash --from xyz
Splitting a Commit
jj split
Resolving Conflicts
jj log
jj resolve
jj resolve --tool meld
Working with Bookmarks (Branches)
jj bookmark create feat-auth
jj bookmark move feat-auth
jj bookmark list
jj git push -b feat-auth
Undoing Mistakes
jj undo
jj op log
jj op restore <op-id>
Common Patterns
"I want to save my work but keep editing"
Just keep editing - it's auto-saved in the working copy commit. Use jj describe to update the message.
"I want to try something without losing current work"
jj new
jj abandon
jj edit @-
"I need to switch contexts"
jj new
jj new other-commit
jj new previous-work
"I accidentally edited the wrong commit"
jj undo
Run Tests After Changes
Always run unit tests after making logic changes to code. This applies regardless of version control system.