jj
can manage a brand-new repo or one that already has a .git
directory. The colocated mode is the most useful for interop — it keeps a real .git/
directory alongside .jj/
, so other tools (gh
, IDE git integrations, GitHub Actions checkout, pre-commit hooks) still work normally.
jj git init --colocate
jj git init --colocate
jj git clone --colocate <git-url> <dir>
jj git init --git-repo <path-to-git-repo> <name>
jj git colocation status
jj git colocation enable
jj git colocation disable
⚠️
Set up.gitignore
BEFORE runningjj git init --colocate
.jj
snapshots the working copy on every command, and anything not gitignored at init time gets swept into the initial change. Cleaning it up afterward is annoying — you have to abandon/squash and re-snapshot, and if you've already pushed, the bad blobs live in the remote history. Common offenders to ignore first: build output dirs (node_modules/
,target/
,build/
,dist/
), secrets (.env
, credentials files), OS junk (.DS_Store
,Thumbs.db
), editor swap/backup files, and large binary artifacts.If you've already done the init without ignoring something, see
Undo an Accidental Working-Copy Snapshotbelow — the recipe is.gitignore
the path first, thenjj file untrack
.
jj config set --user user.name "Your Name"
jj config set --user user.email you@example.com
jj config edit --user
jj config edit --repo
jj config edit --workspace
jj commit -m "Commit message"
jj describe -m "New commit message"
jj edit @
jj new
⚠️
Common pitfall: usingjj describe -m
between push cycles when you meantjj commit -m
.describe
only renames the current change — subsequent edits stack onto the same change, and successivedescribe
/push cycles sideways-overwrite the same commit on origin under different messages while accumulating diff. Looks like N separate commits in your terminal scrollback, but only one commit exists in git history. Usejj commit -m
whenever you want the current change to be done and a fresh empty change ready for the next feature.
jj status
jj log
jj log -n 5
jj diff
jj diff --from <rev1> --to <rev2>
jj diff -r <rev>
jj config set --repo snapshot.auto-track "none()"
jj file track <paths>
jj file untrack <paths>
jj bookmark list
jj bookmark create <name>
jj bookmark create <name> -r @
jj bookmark create <name> && jj edit <name>
jj bookmark set <name> <revision>
jj bookmark set <name> @
jj bookmark set <name> <revision> --allow-backwards
jj bookmark track <name> --remote=<remote>
jj bookmark list --remote <remote>
jj bookmark list --all-remotes # show everything (local + every remote)
Bookmarks are jj's branch equivalent; there is no "current" bookmark. Bookmarks do not move when you create new commits, but they follow rewrites.
jj git fetch
jj git fetch --remote <name>
jj rebase -d <bookmark>@<remote>
jj git export
jj git push
jj git push -b <bookmark_name>
jj git push --change @
jj git push --named myfeature=@
jj git push -b <bookmark_name> --allow-empty-description
jj git push --remote <name> -b <bookmark_name>
jj git import
jj git remote list
jj git remote add <name> <url>
There is no jj git pull
; use jj git fetch
jj rebase -d <bookmark>@<remote>
(or merge).
Set default remotes in .jj/repo/config
so plain jj git fetch
or jj git push
pick the expected remote:
[git]
fetch = "origin"
push = "origin"
jj git remote list
jj git remote add origin <ssh-url>
git switch -c <branch>
jj git export
Notes:
- In a colocated repo,
jj git push
uses jj's remote list, not git's. Add the remote in jj or set[git] fetch/push
in.jj/repo/config
. - If you already have git remotes, copy the SSH URL from
git remote -v
intojj git remote add
.
- Run
jj git fetch
to bring down the latest remote refs. - Use
jj rebase -d <bookmark>@<remote>
(or merge) so your commits sit on the desired remote tip. - In colocated workspaces, jj auto-imports/exports on every command; in non-colocated workspaces, use
jj git export
after jj changes andjj git import
after git changes. - Push with
jj git push -b <bookmark>
— new bookmarks are auto-tracked on push in jj 0.41+; for one-shot WIP/PR,jj git push --change @
auto-creates apush-<changeid>
bookmark and pushes it. - If you moved refs in Git directly, mirror those moves back into jj with
jj git import
.
@
-
Current working copy
@- -
Parent of current working copy
@-- -
Grandparent of current working copy
<bookmark>@<remote> -
Remote-tracking bookmark (e.g.,
main@origin
)<commit_id>
-
Specific commit by ID (prefix is enough to be unique)
<change_id> -
Change ID (shown at the start of
jj log
, stable across rewrites)<bookmark_name>
- Points to commit with that bookmark
jj new
jj squash --from @ --into @-
jj bookmark create feature-branch
jj commit -m "Start work on feature"
jj squash --from <rev> --into <target>
jj rebase -s <start>..<end> -d <target>
jj status
jj resolve <file_path>
jj commit -m "Resolved conflicts"
jj op log
jj undo
jj redo
jj op revert <operation_id>
jj op restore <operation_id>
jj auto-snapshots all non-ignored working-copy files into @
on every
command. Colocating a repo (jj git init --colocate
) or running any jj command while build caches / vendored deps / scratch files are still untracked will pull that junk into your change. Two ways to back it out:
echo 'some-build-dir/' >> .gitignore
jj file untrack some-build-dir scratch some-cache.bin
jj undo # revert the most recent op
jj op restore <operation_id> # rewind to a known-good op (see `jj op log`)
Caveat: jj undo
/ jj op restore
alone will not keep untracked files out — the next jj command re-snapshots them. For stray untracked files the durable fix is always gitignore + jj file untrack (or delete the files). Tested on jj 0.41.
jj restore --from <rev> --into @ <paths>
jj revert -r <rev> -o <dest>
- jj automatically snapshots your working copy before operations
- There is no staging area; most
jj
commands snapshot the working copy automatically - Use
jj op log
+jj undo
/jj redo
orjj op revert
/jj op restore
to recover - Use
jj abandon
to abandon a revision (use--retain-bookmarks
if needed) - Use descriptive bookmark names for easier navigation
jj git
subcommands provide interop with git repositoriesjj log
shows the change ID first, then the commit ID
| Git Command | Jujutsu Equivalent |
|---|---|
git add |
|
Automatic with jj (or jj file track ) |
|
git commit |
|
jj commit -m "message" |
|
git commit --amend |
|
jj new + jj squash --into @- |
|
git checkout branch |
|
jj edit branch_name |
|
git branch |
|
jj bookmark list |
|
git branch name |
|
jj bookmark create name |
|
git push |
|
jj git push |
|
git pull |
|
jj git fetch + jj rebase -d <bookmark>@<remote> |
|
git log |
|
jj log |
|
git diff |
|
jj diff |
|
git rebase |
|
jj rebase |
|
git reset --hard HEAD~1 |
|
jj abandon @ |
|
git stash |
|
| Not needed (auto-snapshots) |