Replies: 3 comments
In fact, you won’t see any letters beyond f in a commit id. |
0 replies
|
Sometimes boxes with words can aid understanding. However, looking through these, I don't see any that help (especially not the "decision tree of commands"). |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Is your feature request related to a problem? Please describe.
I'm new to Jujutsu and wanted to learn it.
Describe the solution you'd like
Visual diagrams are a good way to understand workflows
Describe alternatives you've considered
I used an AI to get some Mermaid diagrams... sharing them here
Additional context
Maybe adding this kind of content could help new user
Here is content I get (this should probably not support all jj features and can be partially wrong or outdated... that's just a try... I'm not a Jujutsu specialist).
Jujutsu (jj) VCS — Core Concepts & Workflows
A visual guide to [Jujutsu](https://github.com/jj-vcs/jj), a Git-compatible version control system that is both simple and powerful.
1. Core Concept: Change ID vs Commit ID
In jj, a Change ID is a stable identifier for your work-in-progress, while the underlying Commit ID (git hash) changes every time you modify the change. This is the foundational concept that makes jj's workflows possible.
graph LR subgraph "Change (stable identity)" CID["Change ID<br/><b>kntqzsqt</b><br/>stays the same"] end CID -->|"edit files"| R1["Revision 1<br/><i>commit</i> a1b2c3d4"] CID -->|"jj describe"| R2["Revision 2<br/><i>commit</i> e5f6a7b8"] CID -->|"edit more files"| R3["Revision 3<br/><i>commit</i> d9c0e1f2"] style CID fill:#4a9eff,stroke:#2670c4,color:#fff style R1 fill:#f0f0f0,stroke:#999 style R2 fill:#f0f0f0,stroke:#999 style R3 fill:#e8ffe8,stroke:#4a4,stroke-width:2px2. Git vs jj Mental Model
The biggest shift from Git to jj: there is no staging area, and the working copy is already a commit. Every
jjcommand auto-snapshots your working directory.graph TB subgraph Git["🔀 Git Mental Model"] direction TB GWD["Working Directory<br/>(untracked changes)"] GIdx["Staging Area / Index<br/>(git add)"] GCommit["Commit<br/>(git commit)"] GBranch["Branch pointer<br/>(HEAD → main)"] GWD -->|"git add"| GIdx GIdx -->|"git commit"| GCommit GCommit -.->|"attached to"| GBranch end subgraph JJ["✨ jj Mental Model"] direction TB JWC["Working Copy<br/>= a real commit<br/>(auto-snapshot)"] JChange["Change<br/>(mutable, stable ID)"] JBook["Bookmark<br/>(optional label)"] JWC -->|"every jj command<br/>auto-amends"| JChange JChange -.->|"optionally labeled"| JBook end style Git fill:#fff5f5,stroke:#c44 style JJ fill:#f0fff0,stroke:#4a4 style GWD fill:#fdd,stroke:#c44 style GIdx fill:#fdd,stroke:#c44 style GCommit fill:#fcc,stroke:#a33 style GBranch fill:#faa,stroke:#833 style JWC fill:#cfc,stroke:#4a4 style JChange fill:#afa,stroke:#383 style JBook fill:#8f8,stroke:#2623. The Squash Workflow
The squash workflow is the preferred workflow of jj's creator, Martin von Zweigbergk. You work in the current change
@, describe it, thenjj newto move on. If you need to fix the parent, make edits andjj squashthem up.graph TB Start["Start: working copy<br/>on empty change @"] Start -->|"1. Write code<br/>(auto-snapshotted)"| WIP["@ has unsaved work<br/>(no description yet)"] WIP -->|"2. jj describe -m 'msg'"| Described["@ described:<br/>'Add user auth'"] Described -->|"3. Keep editing<br/>(auto-amends @)"| More["@ still being<br/>modified"] More -->|"Happy with change?"| Decision{Done?} Decision -->|"Not yet"| More Decision -->|"Yes!"| New["4. jj new<br/>creates fresh empty @<br/>previous becomes parent"] New -->|"Start next task"| Start subgraph "Need to fix previous change?" Oops["Realize parent<br/>needs a tweak"] Oops -->|"Edit files<br/>for the fix"| Fix["Fixes are in<br/>current @"] Fix -->|"jj squash"| Squashed["Moves @ changes<br/>into parent commit"] end New -.->|"Oops, forgot<br/>something"| Oops Squashed -.->|"Continue"| Start style Start fill:#4a9eff,stroke:#2670c4,color:#fff style WIP fill:#fff3cd,stroke:#856404 style Described fill:#d4edda,stroke:#155724 style More fill:#fff3cd,stroke:#856404 style New fill:#cce5ff,stroke:#004085 style Squashed fill:#d4edda,stroke:#155724 style Decision fill:#e2e3e5,stroke:#383d414. The Edit Workflow
The edit workflow is popular among developers who like to plan their changes upfront. You create a series of empty changes with descriptions, then jump between them with
jj editto fill them in. jj auto-rebases descendants as you work.graph TB Plan["1. Plan: Create empty changes<br/>jj new -m 'Add model'<br/>jj new -m 'Add storage'<br/>jj new -m 'Add view'"] Plan --> Skeleton subgraph Skeleton["Commit Graph (planned)"] direction BT S1["📄 Add model<br/><i>(empty)</i>"] S2["📄 Add storage<br/><i>(empty)</i>"] S3["📄 Add view<br/><i>(empty)</i>"] Main["◆ main"] Main --> S1 --> S2 --> S3 end Skeleton -->|"2. jj edit <change-id><br/>Jump to any change"| Edit subgraph Edit["Work on each change"] direction TB E1["Edit 'Add model'<br/>write model code"] E2["jj next --edit<br/>→ 'Add storage'<br/>write storage code"] E3["jj next --edit<br/>→ 'Add view'<br/>write view code"] E1 --> E2 --> E3 end Edit -->|"jj automatically<br/>rebases descendants"| Filled subgraph Filled["Commit Graph (filled)"] direction BT F1["✅ Add model<br/><i>model.py</i>"] F2["✅ Add storage<br/><i>storage.py</i>"] F3["✅ Add view<br/><i>view.py</i>"] FMain["◆ main"] FMain --> F1 --> F2 --> F3 end subgraph Insert["Need to insert a change?"] direction LR INS["jj new -A <change-id><br/>inserts AFTER that change<br/>auto-rebases rest"] end Edit -.-> Insert style Plan fill:#4a9eff,stroke:#2670c4,color:#fff style S1 fill:#f0f0f0,stroke:#999 style S2 fill:#f0f0f0,stroke:#999 style S3 fill:#f0f0f0,stroke:#999 style F1 fill:#d4edda,stroke:#155724 style F2 fill:#d4edda,stroke:#155724 style F3 fill:#d4edda,stroke:#155724 style INS fill:#fff3cd,stroke:#8564045. Branching, Merging & First-Class Conflicts
In jj, branches are anonymous by default — just create a new change from any point. Merging is as simple as
jj new X Y. Conflicts are first-class citizens: a change can be conflicted without blocking your workflow.graph TB subgraph Branching["Anonymous Branches"] direction BT BMain["◆ main"] B1["Change A<br/>'feature-x'"] B2["Change B<br/>'feature-y'"] BMain --> B1 BMain --> B2 Note1["No branch name needed!<br/>Just jj new from any change"] end subgraph Merging["Merging = jj new X Y"] direction BT MMain["◆ main"] M1["Change A"] M2["Change B"] MMerge["Merge change<br/>jj new A B"] MMain --> M1 --> MMerge MMain --> M2 --> MMerge end subgraph Conflicts["First-Class Conflicts"] direction TB C1["Rebase causes<br/>conflict"] C2["⚠️ Change is<br/>marked conflicted<br/>(but it's a valid state!)"] C3["You can:<br/>• switch away<br/>• create children<br/>• fix later"] C4["Resolve when ready<br/>conflicts auto-propagate<br/>to descendants"] C1 --> C2 --> C3 --> C4 end style BMain fill:#888,stroke:#555,color:#fff style MMain fill:#888,stroke:#555,color:#fff style B1 fill:#cce5ff,stroke:#004085 style B2 fill:#f8d7da,stroke:#721c24 style M1 fill:#cce5ff,stroke:#004085 style M2 fill:#f8d7da,stroke:#721c24 style MMerge fill:#d4edda,stroke:#155724 style C2 fill:#fff3cd,stroke:#856404 style C4 fill:#d4edda,stroke:#155724 style Note1 fill:#fff,stroke:#ddd,stroke-dasharray:56. jj → GitHub Workflow (Bookmarks & Push)
To share code via GitHub, you label changes with bookmarks (jj's version of branches), then
jj git push. Updating a PR is as simple as editing the change and pushing again.flowchart TB subgraph Local["Local jj Repository"] direction TB L1["1. Work on changes<br/>(squash or edit workflow)"] L2["2. jj bookmark set my-feature<br/>-r @<br/>label the change"] L3["3. jj git push<br/>pushes bookmark → remote branch"] L1 --> L2 --> L3 end subgraph Remote["GitHub Remote"] direction TB R1["Branch 'my-feature'<br/>appears on GitHub"] R2["Open Pull Request"] R3["Review feedback"] R1 --> R2 --> R3 end L3 -->|"push"| R1 subgraph Update["Responding to PR Feedback"] direction TB U1["jj edit <change-id><br/>go back to the change"] U2["Make fixes<br/>(auto-amends)"] U3["jj git push<br/>force-pushes updated branch"] U1 --> U2 --> U3 end R3 -.->|"need changes"| U1 U3 -->|"updates"| R1 subgraph Sync["Staying in Sync"] direction LR S1["jj git fetch<br/>gets remote changes"] S2["jj rebase -d main<br/>rebase onto latest"] S1 --> S2 end style L1 fill:#cce5ff,stroke:#004085 style L2 fill:#cce5ff,stroke:#004085 style L3 fill:#4a9eff,stroke:#2670c4,color:#fff style R1 fill:#f8d7da,stroke:#721c24 style R2 fill:#f8d7da,stroke:#721c24 style U2 fill:#fff3cd,stroke:#856404 style U3 fill:#4a9eff,stroke:#2670c4,color:#fff style S1 fill:#d4edda,stroke:#155724 style S2 fill:#d4edda,stroke:#1557247. Operation Log — Your Safety Net
Every
jjcommand is recorded in an operation log. You can undo the last operation, or restore to any previous state. This makes experimentation completely risk-free.graph LR subgraph OpLog["Every jj command is recorded"] direction LR O1["op 1<br/>jj new"] O2["op 2<br/>jj describe"] O3["op 3<br/>jj rebase"] O4["op 4<br/>jj squash"] O5["op 5<br/>jj new"] O1 --> O2 --> O3 --> O4 --> O5 end O5 -->|"jj undo"| Undo["Reverts op 5<br/>back to state after op 4"] O3 -->|"jj op restore <op2>"| Restore["Jump back to<br/>state after op 2"] O1 -.->|"jj op log"| View["View full<br/>operation history"] style O1 fill:#e8e8e8,stroke:#999 style O2 fill:#e8e8e8,stroke:#999 style O3 fill:#e8e8e8,stroke:#999 style O4 fill:#e8e8e8,stroke:#999 style O5 fill:#cce5ff,stroke:#004085 style Undo fill:#d4edda,stroke:#155724 style Restore fill:#fff3cd,stroke:#856404 style View fill:#f0f0f0,stroke:#9998. jj Command Decision Flowchart
A quick reference to find the right
jjcommand for what you want to do.flowchart TD Start(("What do you<br/>want to do?")) Start --> Create["Create / Navigate"] Start --> Modify["Modify Changes"] Start --> History["View History"] Start --> Share["Share Code"] Start --> Fix["Fix Mistakes"] Create --> C1["Start new change → <b>jj new</b>"] Create --> C2["Clone a repo → <b>jj git clone</b>"] Create --> C3["Init in existing git → <b>jj git init --colocate</b>"] Create --> C4["Jump to a change → <b>jj edit CHANGE_ID</b>"] Create --> C5["Create merge → <b>jj new X Y</b>"] Modify --> M1["Set commit message → <b>jj describe -m '...'</b>"] Modify --> M2["Move changes to parent → <b>jj squash</b>"] Modify --> M3["Split a change → <b>jj split</b>"] Modify --> M4["Move change in graph → <b>jj rebase -r X -d Y</b>"] Modify --> M5["Absorb fixes into ancestors → <b>jj absorb</b>"] Modify --> M6["Insert change after X → <b>jj new -A X</b>"] Modify --> M7["Restore file from another change → <b>jj restore</b>"] History --> H1["View log → <b>jj log</b>"] History --> H2["View diff → <b>jj diff</b>"] History --> H3["View status → <b>jj st</b>"] History --> H4["View operations → <b>jj op log</b>"] Share --> S1["Create bookmark → <b>jj bookmark set NAME</b>"] Share --> S2["Push to remote → <b>jj git push</b>"] Share --> S3["Fetch from remote → <b>jj git fetch</b>"] Share --> S4["Rebase onto main → <b>jj rebase -d main</b>"] Fix --> F1["Undo last operation → <b>jj undo</b>"] Fix --> F2["Restore to any op → <b>jj op restore</b>"] Fix --> F3["View change evolution → <b>jj obslog</b>"] style Start fill:#4a9eff,stroke:#2670c4,color:#fff style Create fill:#cce5ff,stroke:#004085 style Modify fill:#fff3cd,stroke:#856404 style History fill:#d4edda,stroke:#155724 style Share fill:#f8d7da,stroke:#721c24 style Fix fill:#e2d5f1,stroke:#6f42c1All reactions